// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/backend/instruction-selector.h"

#include <limits>

#include "src/assembler-inl.h"
#include "src/base/adapters.h"
#include "src/compiler/backend/instruction-selector-impl.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/pipeline.h"
#include "src/compiler/schedule.h"
#include "src/compiler/state-values-utils.h"
#include "src/deoptimizer.h"

namespace v8 {
namespace internal {
    namespace compiler {

        InstructionSelector::InstructionSelector(
            Zone* zone, size_t node_count, Linkage* linkage,
            InstructionSequence* sequence, Schedule* schedule,
            SourcePositionTable* source_positions, Frame* frame,
            EnableSwitchJumpTable enable_switch_jump_table,
            SourcePositionMode source_position_mode, Features features,
            EnableScheduling enable_scheduling,
            EnableRootsRelativeAddressing enable_roots_relative_addressing,
            PoisoningMitigationLevel poisoning_level, EnableTraceTurboJson trace_turbo)
            : zone_(zone)
            , linkage_(linkage)
            , sequence_(sequence)
            , source_positions_(source_positions)
            , source_position_mode_(source_position_mode)
            , features_(features)
            , schedule_(schedule)
            , current_block_(nullptr)
            , instructions_(zone)
            , continuation_inputs_(sequence->zone())
            , continuation_outputs_(sequence->zone())
            , defined_(node_count, false, zone)
            , used_(node_count, false, zone)
            , effect_level_(node_count, 0, zone)
            , virtual_registers_(node_count,
                  InstructionOperand::kInvalidVirtualRegister, zone)
            , virtual_register_rename_(zone)
            , scheduler_(nullptr)
            , enable_scheduling_(enable_scheduling)
            , enable_roots_relative_addressing_(enable_roots_relative_addressing)
            , enable_switch_jump_table_(enable_switch_jump_table)
            , poisoning_level_(poisoning_level)
            , frame_(frame)
            , instruction_selection_failed_(false)
            , instr_origins_(sequence->zone())
            , trace_turbo_(trace_turbo)
        {
            instructions_.reserve(node_count);
            continuation_inputs_.reserve(5);
            continuation_outputs_.reserve(2);

            if (trace_turbo_ == kEnableTraceTurboJson) {
                instr_origins_.assign(node_count, { -1, 0 });
            }
        }

        bool InstructionSelector::SelectInstructions()
        {
            // Mark the inputs of all phis in loop headers as used.
            BasicBlockVector* blocks = schedule()->rpo_order();
            for (auto const block : *blocks) {
                if (!block->IsLoopHeader())
                    continue;
                DCHECK_LE(2u, block->PredecessorCount());
                for (Node* const phi : *block) {
                    if (phi->opcode() != IrOpcode::kPhi)
                        continue;

                    // Mark all inputs as used.
                    for (Node* const input : phi->inputs()) {
                        MarkAsUsed(input);
                    }
                }
            }

            // Visit each basic block in post order.
            for (auto i = blocks->rbegin(); i != blocks->rend(); ++i) {
                VisitBlock(*i);
                if (instruction_selection_failed())
                    return false;
            }

            // Schedule the selected instructions.
            if (UseInstructionScheduling()) {
                scheduler_ = new (zone()) InstructionScheduler(zone(), sequence());
            }

            for (auto const block : *blocks) {
                InstructionBlock* instruction_block = sequence()->InstructionBlockAt(RpoNumber::FromInt(block->rpo_number()));
                for (size_t i = 0; i < instruction_block->phis().size(); i++) {
                    UpdateRenamesInPhi(instruction_block->PhiAt(i));
                }
                size_t end = instruction_block->code_end();
                size_t start = instruction_block->code_start();
                DCHECK_LE(end, start);
                StartBlock(RpoNumber::FromInt(block->rpo_number()));
                if (end != start) {
                    while (start-- > end + 1) {
                        UpdateRenames(instructions_[start]);
                        AddInstruction(instructions_[start]);
                    }
                    UpdateRenames(instructions_[end]);
                    AddTerminator(instructions_[end]);
                }
                EndBlock(RpoNumber::FromInt(block->rpo_number()));
            }
#if DEBUG
            sequence()->ValidateSSA();
#endif
            return true;
        }

        void InstructionSelector::StartBlock(RpoNumber rpo)
        {
            if (UseInstructionScheduling()) {
                DCHECK_NOT_NULL(scheduler_);
                scheduler_->StartBlock(rpo);
            } else {
                sequence()->StartBlock(rpo);
            }
        }

        void InstructionSelector::EndBlock(RpoNumber rpo)
        {
            if (UseInstructionScheduling()) {
                DCHECK_NOT_NULL(scheduler_);
                scheduler_->EndBlock(rpo);
            } else {
                sequence()->EndBlock(rpo);
            }
        }

        void InstructionSelector::AddTerminator(Instruction* instr)
        {
            if (UseInstructionScheduling()) {
                DCHECK_NOT_NULL(scheduler_);
                scheduler_->AddTerminator(instr);
            } else {
                sequence()->AddInstruction(instr);
            }
        }

        void InstructionSelector::AddInstruction(Instruction* instr)
        {
            if (UseInstructionScheduling()) {
                DCHECK_NOT_NULL(scheduler_);
                scheduler_->AddInstruction(instr);
            } else {
                sequence()->AddInstruction(instr);
            }
        }

        Instruction* InstructionSelector::Emit(InstructionCode opcode,
            InstructionOperand output,
            size_t temp_count,
            InstructionOperand* temps)
        {
            size_t output_count = output.IsInvalid() ? 0 : 1;
            return Emit(opcode, output_count, &output, 0, nullptr, temp_count, temps);
        }

        Instruction* InstructionSelector::Emit(InstructionCode opcode,
            InstructionOperand output,
            InstructionOperand a, size_t temp_count,
            InstructionOperand* temps)
        {
            size_t output_count = output.IsInvalid() ? 0 : 1;
            return Emit(opcode, output_count, &output, 1, &a, temp_count, temps);
        }

        Instruction* InstructionSelector::Emit(InstructionCode opcode,
            InstructionOperand output,
            InstructionOperand a,
            InstructionOperand b, size_t temp_count,
            InstructionOperand* temps)
        {
            size_t output_count = output.IsInvalid() ? 0 : 1;
            InstructionOperand inputs[] = { a, b };
            size_t input_count = arraysize(inputs);
            return Emit(opcode, output_count, &output, input_count, inputs, temp_count,
                temps);
        }

        Instruction* InstructionSelector::Emit(InstructionCode opcode,
            InstructionOperand output,
            InstructionOperand a,
            InstructionOperand b,
            InstructionOperand c, size_t temp_count,
            InstructionOperand* temps)
        {
            size_t output_count = output.IsInvalid() ? 0 : 1;
            InstructionOperand inputs[] = { a, b, c };
            size_t input_count = arraysize(inputs);
            return Emit(opcode, output_count, &output, input_count, inputs, temp_count,
                temps);
        }

        Instruction* InstructionSelector::Emit(
            InstructionCode opcode, InstructionOperand output, InstructionOperand a,
            InstructionOperand b, InstructionOperand c, InstructionOperand d,
            size_t temp_count, InstructionOperand* temps)
        {
            size_t output_count = output.IsInvalid() ? 0 : 1;
            InstructionOperand inputs[] = { a, b, c, d };
            size_t input_count = arraysize(inputs);
            return Emit(opcode, output_count, &output, input_count, inputs, temp_count,
                temps);
        }

        Instruction* InstructionSelector::Emit(
            InstructionCode opcode, InstructionOperand output, InstructionOperand a,
            InstructionOperand b, InstructionOperand c, InstructionOperand d,
            InstructionOperand e, size_t temp_count, InstructionOperand* temps)
        {
            size_t output_count = output.IsInvalid() ? 0 : 1;
            InstructionOperand inputs[] = { a, b, c, d, e };
            size_t input_count = arraysize(inputs);
            return Emit(opcode, output_count, &output, input_count, inputs, temp_count,
                temps);
        }

        Instruction* InstructionSelector::Emit(
            InstructionCode opcode, InstructionOperand output, InstructionOperand a,
            InstructionOperand b, InstructionOperand c, InstructionOperand d,
            InstructionOperand e, InstructionOperand f, size_t temp_count,
            InstructionOperand* temps)
        {
            size_t output_count = output.IsInvalid() ? 0 : 1;
            InstructionOperand inputs[] = { a, b, c, d, e, f };
            size_t input_count = arraysize(inputs);
            return Emit(opcode, output_count, &output, input_count, inputs, temp_count,
                temps);
        }

        Instruction* InstructionSelector::Emit(
            InstructionCode opcode, size_t output_count, InstructionOperand* outputs,
            size_t input_count, InstructionOperand* inputs, size_t temp_count,
            InstructionOperand* temps)
        {
            if (output_count >= Instruction::kMaxOutputCount || input_count >= Instruction::kMaxInputCount || temp_count >= Instruction::kMaxTempCount) {
                set_instruction_selection_failed();
                return nullptr;
            }

            Instruction* instr = Instruction::New(instruction_zone(), opcode, output_count, outputs,
                input_count, inputs, temp_count, temps);
            return Emit(instr);
        }

        Instruction* InstructionSelector::Emit(Instruction* instr)
        {
            instructions_.push_back(instr);
            return instr;
        }

        bool InstructionSelector::CanCover(Node* user, Node* node) const
        {
            // 1. Both {user} and {node} must be in the same basic block.
            if (schedule()->block(node) != schedule()->block(user)) {
                return false;
            }
            // 2. Pure {node}s must be owned by the {user}.
            if (node->op()->HasProperty(Operator::kPure)) {
                return node->OwnedBy(user);
            }
            // 3. Impure {node}s must match the effect level of {user}.
            if (GetEffectLevel(node) != GetEffectLevel(user)) {
                return false;
            }
            // 4. Only {node} must have value edges pointing to {user}.
            for (Edge const edge : node->use_edges()) {
                if (edge.from() != user && NodeProperties::IsValueEdge(edge)) {
                    return false;
                }
            }
            return true;
        }

        bool InstructionSelector::CanCoverTransitively(Node* user, Node* node,
            Node* node_input) const
        {
            if (CanCover(user, node) && CanCover(node, node_input)) {
                // If {node} is pure, transitivity might not hold.
                if (node->op()->HasProperty(Operator::kPure)) {
                    // If {node_input} is pure, the effect levels do not matter.
                    if (node_input->op()->HasProperty(Operator::kPure))
                        return true;
                    // Otherwise, {user} and {node_input} must have the same effect level.
                    return GetEffectLevel(user) == GetEffectLevel(node_input);
                }
                return true;
            }
            return false;
        }

        bool InstructionSelector::IsOnlyUserOfNodeInSameBlock(Node* user,
            Node* node) const
        {
            BasicBlock* bb_user = schedule()->block(user);
            BasicBlock* bb_node = schedule()->block(node);
            if (bb_user != bb_node)
                return false;
            for (Edge const edge : node->use_edges()) {
                Node* from = edge.from();
                if ((from != user) && (schedule()->block(from) == bb_user)) {
                    return false;
                }
            }
            return true;
        }

        void InstructionSelector::UpdateRenames(Instruction* instruction)
        {
            for (size_t i = 0; i < instruction->InputCount(); i++) {
                TryRename(instruction->InputAt(i));
            }
        }

        void InstructionSelector::UpdateRenamesInPhi(PhiInstruction* phi)
        {
            for (size_t i = 0; i < phi->operands().size(); i++) {
                int vreg = phi->operands()[i];
                int renamed = GetRename(vreg);
                if (vreg != renamed) {
                    phi->RenameInput(i, renamed);
                }
            }
        }

        int InstructionSelector::GetRename(int virtual_register)
        {
            int rename = virtual_register;
            while (true) {
                if (static_cast<size_t>(rename) >= virtual_register_rename_.size())
                    break;
                int next = virtual_register_rename_[rename];
                if (next == InstructionOperand::kInvalidVirtualRegister) {
                    break;
                }
                rename = next;
            }
            return rename;
        }

        void InstructionSelector::TryRename(InstructionOperand* op)
        {
            if (!op->IsUnallocated())
                return;
            UnallocatedOperand* unalloc = UnallocatedOperand::cast(op);
            int vreg = unalloc->virtual_register();
            int rename = GetRename(vreg);
            if (rename != vreg) {
                *unalloc = UnallocatedOperand(*unalloc, rename);
            }
        }

        void InstructionSelector::SetRename(const Node* node, const Node* rename)
        {
            int vreg = GetVirtualRegister(node);
            if (static_cast<size_t>(vreg) >= virtual_register_rename_.size()) {
                int invalid = InstructionOperand::kInvalidVirtualRegister;
                virtual_register_rename_.resize(vreg + 1, invalid);
            }
            virtual_register_rename_[vreg] = GetVirtualRegister(rename);
        }

        int InstructionSelector::GetVirtualRegister(const Node* node)
        {
            DCHECK_NOT_NULL(node);
            size_t const id = node->id();
            DCHECK_LT(id, virtual_registers_.size());
            int virtual_register = virtual_registers_[id];
            if (virtual_register == InstructionOperand::kInvalidVirtualRegister) {
                virtual_register = sequence()->NextVirtualRegister();
                virtual_registers_[id] = virtual_register;
            }
            return virtual_register;
        }

        const std::map<NodeId, int> InstructionSelector::GetVirtualRegistersForTesting()
            const
        {
            std::map<NodeId, int> virtual_registers;
            for (size_t n = 0; n < virtual_registers_.size(); ++n) {
                if (virtual_registers_[n] != InstructionOperand::kInvalidVirtualRegister) {
                    NodeId const id = static_cast<NodeId>(n);
                    virtual_registers.insert(std::make_pair(id, virtual_registers_[n]));
                }
            }
            return virtual_registers;
        }

        bool InstructionSelector::IsDefined(Node* node) const
        {
            DCHECK_NOT_NULL(node);
            size_t const id = node->id();
            DCHECK_LT(id, defined_.size());
            return defined_[id];
        }

        void InstructionSelector::MarkAsDefined(Node* node)
        {
            DCHECK_NOT_NULL(node);
            size_t const id = node->id();
            DCHECK_LT(id, defined_.size());
            defined_[id] = true;
        }

        bool InstructionSelector::IsUsed(Node* node) const
        {
            DCHECK_NOT_NULL(node);
            // TODO(bmeurer): This is a terrible monster hack, but we have to make sure
            // that the Retain is actually emitted, otherwise the GC will mess up.
            if (node->opcode() == IrOpcode::kRetain)
                return true;
            if (!node->op()->HasProperty(Operator::kEliminatable))
                return true;
            size_t const id = node->id();
            DCHECK_LT(id, used_.size());
            return used_[id];
        }

        void InstructionSelector::MarkAsUsed(Node* node)
        {
            DCHECK_NOT_NULL(node);
            size_t const id = node->id();
            DCHECK_LT(id, used_.size());
            used_[id] = true;
        }

        int InstructionSelector::GetEffectLevel(Node* node) const
        {
            DCHECK_NOT_NULL(node);
            size_t const id = node->id();
            DCHECK_LT(id, effect_level_.size());
            return effect_level_[id];
        }

        void InstructionSelector::SetEffectLevel(Node* node, int effect_level)
        {
            DCHECK_NOT_NULL(node);
            size_t const id = node->id();
            DCHECK_LT(id, effect_level_.size());
            effect_level_[id] = effect_level;
        }

        bool InstructionSelector::CanAddressRelativeToRootsRegister() const
        {
            return enable_roots_relative_addressing_ == kEnableRootsRelativeAddressing && CanUseRootsRegister();
        }

        bool InstructionSelector::CanUseRootsRegister() const
        {
            return linkage()->GetIncomingDescriptor()->flags() & CallDescriptor::kCanUseRoots;
        }

        void InstructionSelector::MarkAsRepresentation(MachineRepresentation rep,
            const InstructionOperand& op)
        {
            UnallocatedOperand unalloc = UnallocatedOperand::cast(op);
            sequence()->MarkAsRepresentation(rep, unalloc.virtual_register());
        }

        void InstructionSelector::MarkAsRepresentation(MachineRepresentation rep,
            Node* node)
        {
            sequence()->MarkAsRepresentation(rep, GetVirtualRegister(node));
        }

        namespace {

            InstructionOperand OperandForDeopt(Isolate* isolate, OperandGenerator* g,
                Node* input, FrameStateInputKind kind,
                MachineRepresentation rep)
            {
                if (rep == MachineRepresentation::kNone) {
                    return g->TempImmediate(FrameStateDescriptor::kImpossibleValue);
                }

                switch (input->opcode()) {
                case IrOpcode::kInt32Constant:
                case IrOpcode::kInt64Constant:
                case IrOpcode::kNumberConstant:
                case IrOpcode::kFloat32Constant:
                case IrOpcode::kFloat64Constant:
                case IrOpcode::kDelayedStringConstant:
                    return g->UseImmediate(input);
                case IrOpcode::kHeapConstant: {
                    if (!CanBeTaggedPointer(rep)) {
                        // If we have inconsistent static and dynamic types, e.g. if we
                        // smi-check a string, we can get here with a heap object that
                        // says it is a smi. In that case, we return an invalid instruction
                        // operand, which will be interpreted as an optimized-out value.

                        // TODO(jarin) Ideally, we should turn the current instruction
                        // into an abort (we should never execute it).
                        return InstructionOperand();
                    }

                    Handle<HeapObject> constant = HeapConstantOf(input->op());
                    RootIndex root_index;
                    if (isolate->roots_table().IsRootHandle(constant, &root_index) && root_index == RootIndex::kOptimizedOut) {
                        // For an optimized-out object we return an invalid instruction
                        // operand, so that we take the fast path for optimized-out values.
                        return InstructionOperand();
                    }

                    return g->UseImmediate(input);
                }
                case IrOpcode::kArgumentsElementsState:
                case IrOpcode::kArgumentsLengthState:
                case IrOpcode::kObjectState:
                case IrOpcode::kTypedObjectState:
                    UNREACHABLE();
                    break;
                default:
                    switch (kind) {
                    case FrameStateInputKind::kStackSlot:
                        return g->UseUniqueSlot(input);
                    case FrameStateInputKind::kAny:
                        // Currently deopts "wrap" other operations, so the deopt's inputs
                        // are potentially needed until the end of the deoptimising code.
                        return g->UseAnyAtEnd(input);
                    }
                }
                UNREACHABLE();
            }

        } // namespace

        class StateObjectDeduplicator {
        public:
            explicit StateObjectDeduplicator(Zone* zone)
                : objects_(zone)
            {
            }
            static const size_t kNotDuplicated = SIZE_MAX;

            size_t GetObjectId(Node* node)
            {
                DCHECK(node->opcode() == IrOpcode::kTypedObjectState || node->opcode() == IrOpcode::kObjectId || node->opcode() == IrOpcode::kArgumentsElementsState);
                for (size_t i = 0; i < objects_.size(); ++i) {
                    if (objects_[i] == node)
                        return i;
                    // ObjectId nodes are the Turbofan way to express objects with the same
                    // identity in the deopt info. So they should always be mapped to
                    // previously appearing TypedObjectState nodes.
                    if (HasObjectId(objects_[i]) && HasObjectId(node) && ObjectIdOf(objects_[i]->op()) == ObjectIdOf(node->op())) {
                        return i;
                    }
                }
                DCHECK(node->opcode() == IrOpcode::kTypedObjectState || node->opcode() == IrOpcode::kArgumentsElementsState);
                return kNotDuplicated;
            }

            size_t InsertObject(Node* node)
            {
                DCHECK(node->opcode() == IrOpcode::kTypedObjectState || node->opcode() == IrOpcode::kObjectId || node->opcode() == IrOpcode::kArgumentsElementsState);
                size_t id = objects_.size();
                objects_.push_back(node);
                return id;
            }

        private:
            static bool HasObjectId(Node* node)
            {
                return node->opcode() == IrOpcode::kTypedObjectState || node->opcode() == IrOpcode::kObjectId;
            }

            ZoneVector<Node*> objects_;
        };

        // Returns the number of instruction operands added to inputs.
        size_t InstructionSelector::AddOperandToStateValueDescriptor(
            StateValueList* values, InstructionOperandVector* inputs,
            OperandGenerator* g, StateObjectDeduplicator* deduplicator, Node* input,
            MachineType type, FrameStateInputKind kind, Zone* zone)
        {
            if (input == nullptr) {
                values->PushOptimizedOut();
                return 0;
            }

            switch (input->opcode()) {
            case IrOpcode::kArgumentsElementsState: {
                values->PushArgumentsElements(ArgumentsStateTypeOf(input->op()));
                // The elements backing store of an arguments object participates in the
                // duplicate object counting, but can itself never appear duplicated.
                DCHECK_EQ(StateObjectDeduplicator::kNotDuplicated,
                    deduplicator->GetObjectId(input));
                deduplicator->InsertObject(input);
                return 0;
            }
            case IrOpcode::kArgumentsLengthState: {
                values->PushArgumentsLength(ArgumentsStateTypeOf(input->op()));
                return 0;
            }
            case IrOpcode::kObjectState: {
                UNREACHABLE();
            }
            case IrOpcode::kTypedObjectState:
            case IrOpcode::kObjectId: {
                size_t id = deduplicator->GetObjectId(input);
                if (id == StateObjectDeduplicator::kNotDuplicated) {
                    DCHECK_EQ(IrOpcode::kTypedObjectState, input->opcode());
                    size_t entries = 0;
                    id = deduplicator->InsertObject(input);
                    StateValueList* nested = values->PushRecursiveField(zone, id);
                    int const input_count = input->op()->ValueInputCount();
                    ZoneVector<MachineType> const* types = MachineTypesOf(input->op());
                    for (int i = 0; i < input_count; ++i) {
                        entries += AddOperandToStateValueDescriptor(
                            nested, inputs, g, deduplicator, input->InputAt(i), types->at(i),
                            kind, zone);
                    }
                    return entries;
                } else {
                    // Deoptimizer counts duplicate objects for the running id, so we have
                    // to push the input again.
                    deduplicator->InsertObject(input);
                    values->PushDuplicate(id);
                    return 0;
                }
            }
            default: {
                InstructionOperand op = OperandForDeopt(isolate(), g, input, kind, type.representation());
                if (op.kind() == InstructionOperand::INVALID) {
                    // Invalid operand means the value is impossible or optimized-out.
                    values->PushOptimizedOut();
                    return 0;
                } else {
                    inputs->push_back(op);
                    values->PushPlain(type);
                    return 1;
                }
            }
            }
        }

        // Returns the number of instruction operands added to inputs.
        size_t InstructionSelector::AddInputsToFrameStateDescriptor(
            FrameStateDescriptor* descriptor, Node* state, OperandGenerator* g,
            StateObjectDeduplicator* deduplicator, InstructionOperandVector* inputs,
            FrameStateInputKind kind, Zone* zone)
        {
            DCHECK_EQ(IrOpcode::kFrameState, state->op()->opcode());

            size_t entries = 0;
            size_t initial_size = inputs->size();
            USE(initial_size); // initial_size is only used for debug.

            if (descriptor->outer_state()) {
                entries += AddInputsToFrameStateDescriptor(
                    descriptor->outer_state(), state->InputAt(kFrameStateOuterStateInput),
                    g, deduplicator, inputs, kind, zone);
            }

            Node* parameters = state->InputAt(kFrameStateParametersInput);
            Node* locals = state->InputAt(kFrameStateLocalsInput);
            Node* stack = state->InputAt(kFrameStateStackInput);
            Node* context = state->InputAt(kFrameStateContextInput);
            Node* function = state->InputAt(kFrameStateFunctionInput);

            DCHECK_EQ(descriptor->parameters_count(),
                StateValuesAccess(parameters).size());
            DCHECK_EQ(descriptor->locals_count(), StateValuesAccess(locals).size());
            DCHECK_EQ(descriptor->stack_count(), StateValuesAccess(stack).size());

            StateValueList* values_descriptor = descriptor->GetStateValueDescriptors();

            DCHECK_EQ(values_descriptor->size(), 0u);
            values_descriptor->ReserveSize(descriptor->GetSize());

            entries += AddOperandToStateValueDescriptor(
                values_descriptor, inputs, g, deduplicator, function,
                MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
            for (StateValuesAccess::TypedNode input_node :
                StateValuesAccess(parameters)) {
                entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
                    deduplicator, input_node.node,
                    input_node.type, kind, zone);
            }
            if (descriptor->HasContext()) {
                entries += AddOperandToStateValueDescriptor(
                    values_descriptor, inputs, g, deduplicator, context,
                    MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
            }
            for (StateValuesAccess::TypedNode input_node : StateValuesAccess(locals)) {
                entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
                    deduplicator, input_node.node,
                    input_node.type, kind, zone);
            }
            for (StateValuesAccess::TypedNode input_node : StateValuesAccess(stack)) {
                entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
                    deduplicator, input_node.node,
                    input_node.type, kind, zone);
            }
            DCHECK_EQ(initial_size + entries, inputs->size());
            return entries;
        }

        Instruction* InstructionSelector::EmitWithContinuation(
            InstructionCode opcode, FlagsContinuation* cont)
        {
            return EmitWithContinuation(opcode, 0, nullptr, 0, nullptr, cont);
        }

        Instruction* InstructionSelector::EmitWithContinuation(
            InstructionCode opcode, InstructionOperand a, FlagsContinuation* cont)
        {
            return EmitWithContinuation(opcode, 0, nullptr, 1, &a, cont);
        }

        Instruction* InstructionSelector::EmitWithContinuation(
            InstructionCode opcode, InstructionOperand a, InstructionOperand b,
            FlagsContinuation* cont)
        {
            InstructionOperand inputs[] = { a, b };
            return EmitWithContinuation(opcode, 0, nullptr, arraysize(inputs), inputs,
                cont);
        }

        Instruction* InstructionSelector::EmitWithContinuation(
            InstructionCode opcode, InstructionOperand a, InstructionOperand b,
            InstructionOperand c, FlagsContinuation* cont)
        {
            InstructionOperand inputs[] = { a, b, c };
            return EmitWithContinuation(opcode, 0, nullptr, arraysize(inputs), inputs,
                cont);
        }

        Instruction* InstructionSelector::EmitWithContinuation(
            InstructionCode opcode, size_t output_count, InstructionOperand* outputs,
            size_t input_count, InstructionOperand* inputs, FlagsContinuation* cont)
        {
            OperandGenerator g(this);

            opcode = cont->Encode(opcode);

            continuation_inputs_.resize(0);
            for (size_t i = 0; i < input_count; i++) {
                continuation_inputs_.push_back(inputs[i]);
            }

            continuation_outputs_.resize(0);
            for (size_t i = 0; i < output_count; i++) {
                continuation_outputs_.push_back(outputs[i]);
            }

            if (cont->IsBranch()) {
                continuation_inputs_.push_back(g.Label(cont->true_block()));
                continuation_inputs_.push_back(g.Label(cont->false_block()));
            } else if (cont->IsDeoptimize()) {
                opcode |= MiscField::encode(static_cast<int>(input_count));
                AppendDeoptimizeArguments(&continuation_inputs_, cont->kind(),
                    cont->reason(), cont->feedback(),
                    cont->frame_state());
            } else if (cont->IsSet()) {
                continuation_outputs_.push_back(g.DefineAsRegister(cont->result()));
            } else if (cont->IsTrap()) {
                int trap_id = static_cast<int>(cont->trap_id());
                continuation_inputs_.push_back(g.UseImmediate(trap_id));
            } else {
                DCHECK(cont->IsNone());
            }

            size_t const emit_inputs_size = continuation_inputs_.size();
            auto* emit_inputs = emit_inputs_size ? &continuation_inputs_.front() : nullptr;
            size_t const emit_outputs_size = continuation_outputs_.size();
            auto* emit_outputs = emit_outputs_size ? &continuation_outputs_.front() : nullptr;
            return Emit(opcode, emit_outputs_size, emit_outputs, emit_inputs_size,
                emit_inputs, 0, nullptr);
        }

        void InstructionSelector::AppendDeoptimizeArguments(
            InstructionOperandVector* args, DeoptimizeKind kind,
            DeoptimizeReason reason, VectorSlotPair const& feedback,
            Node* frame_state)
        {
            OperandGenerator g(this);
            FrameStateDescriptor* const descriptor = GetFrameStateDescriptor(frame_state);
            DCHECK_NE(DeoptimizeKind::kLazy, kind);
            int const state_id = sequence()->AddDeoptimizationEntry(descriptor, kind, reason, feedback);
            args->push_back(g.TempImmediate(state_id));
            StateObjectDeduplicator deduplicator(instruction_zone());
            AddInputsToFrameStateDescriptor(descriptor, frame_state, &g, &deduplicator,
                args, FrameStateInputKind::kAny,
                instruction_zone());
        }

        Instruction* InstructionSelector::EmitDeoptimize(
            InstructionCode opcode, size_t output_count, InstructionOperand* outputs,
            size_t input_count, InstructionOperand* inputs, DeoptimizeKind kind,
            DeoptimizeReason reason, VectorSlotPair const& feedback,
            Node* frame_state)
        {
            InstructionOperandVector args(instruction_zone());
            for (size_t i = 0; i < input_count; ++i) {
                args.push_back(inputs[i]);
            }
            opcode |= MiscField::encode(static_cast<int>(input_count));
            AppendDeoptimizeArguments(&args, kind, reason, feedback, frame_state);
            return Emit(opcode, output_count, outputs, args.size(), &args.front(), 0,
                nullptr);
        }

        // An internal helper class for generating the operands to calls.
        // TODO(bmeurer): Get rid of the CallBuffer business and make
        // InstructionSelector::VisitCall platform independent instead.
        struct CallBuffer {
            CallBuffer(Zone* zone, const CallDescriptor* call_descriptor,
                FrameStateDescriptor* frame_state)
                : descriptor(call_descriptor)
                , frame_state_descriptor(frame_state)
                , output_nodes(zone)
                , outputs(zone)
                , instruction_args(zone)
                , pushed_nodes(zone)
            {
                output_nodes.reserve(call_descriptor->ReturnCount());
                outputs.reserve(call_descriptor->ReturnCount());
                pushed_nodes.reserve(input_count());
                instruction_args.reserve(input_count() + frame_state_value_count());
            }

            const CallDescriptor* descriptor;
            FrameStateDescriptor* frame_state_descriptor;
            ZoneVector<PushParameter> output_nodes;
            InstructionOperandVector outputs;
            InstructionOperandVector instruction_args;
            ZoneVector<PushParameter> pushed_nodes;

            size_t input_count() const { return descriptor->InputCount(); }

            size_t frame_state_count() const { return descriptor->FrameStateCount(); }

            size_t frame_state_value_count() const
            {
                return (frame_state_descriptor == nullptr)
                    ? 0
                    : (frame_state_descriptor->GetTotalSize() + 1); // Include deopt id.
            }
        };

        // TODO(bmeurer): Get rid of the CallBuffer business and make
        // InstructionSelector::VisitCall platform independent instead.
        void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
            CallBufferFlags flags,
            bool is_tail_call,
            int stack_param_delta)
        {
            OperandGenerator g(this);
            size_t ret_count = buffer->descriptor->ReturnCount();
            DCHECK_LE(call->op()->ValueOutputCount(), ret_count);
            DCHECK_EQ(
                call->op()->ValueInputCount(),
                static_cast<int>(buffer->input_count() + buffer->frame_state_count()));

            if (ret_count > 0) {
                // Collect the projections that represent multiple outputs from this call.
                if (ret_count == 1) {
                    PushParameter result = { call, buffer->descriptor->GetReturnLocation(0) };
                    buffer->output_nodes.push_back(result);
                } else {
                    buffer->output_nodes.resize(ret_count);
                    int stack_count = 0;
                    for (size_t i = 0; i < ret_count; ++i) {
                        LinkageLocation location = buffer->descriptor->GetReturnLocation(i);
                        buffer->output_nodes[i] = PushParameter(nullptr, location);
                        if (location.IsCallerFrameSlot()) {
                            stack_count += location.GetSizeInPointers();
                        }
                    }
                    for (Edge const edge : call->use_edges()) {
                        if (!NodeProperties::IsValueEdge(edge))
                            continue;
                        Node* node = edge.from();
                        DCHECK_EQ(IrOpcode::kProjection, node->opcode());
                        size_t const index = ProjectionIndexOf(node->op());

                        DCHECK_LT(index, buffer->output_nodes.size());
                        DCHECK(!buffer->output_nodes[index].node);
                        buffer->output_nodes[index].node = node;
                    }
                    frame_->EnsureReturnSlots(stack_count);
                }

                // Filter out the outputs that aren't live because no projection uses them.
                size_t outputs_needed_by_framestate = buffer->frame_state_descriptor == nullptr
                    ? 0
                    : buffer->frame_state_descriptor->state_combine()
                          .ConsumedOutputCount();
                for (size_t i = 0; i < buffer->output_nodes.size(); i++) {
                    bool output_is_live = buffer->output_nodes[i].node != nullptr || i < outputs_needed_by_framestate;
                    if (output_is_live) {
                        LinkageLocation location = buffer->output_nodes[i].location;
                        MachineRepresentation rep = location.GetType().representation();

                        Node* output = buffer->output_nodes[i].node;
                        InstructionOperand op = output == nullptr
                            ? g.TempLocation(location)
                            : g.DefineAsLocation(output, location);
                        MarkAsRepresentation(rep, op);

                        if (!UnallocatedOperand::cast(op).HasFixedSlotPolicy()) {
                            buffer->outputs.push_back(op);
                            buffer->output_nodes[i].node = nullptr;
                        }
                    }
                }
            }

            // The first argument is always the callee code.
            Node* callee = call->InputAt(0);
            bool call_code_immediate = (flags & kCallCodeImmediate) != 0;
            bool call_address_immediate = (flags & kCallAddressImmediate) != 0;
            bool call_use_fixed_target_reg = (flags & kCallFixedTargetRegister) != 0;
            bool call_through_slot = (flags & kAllowCallThroughSlot) != 0;
            switch (buffer->descriptor->kind()) {
            case CallDescriptor::kCallCodeObject:
                // TODO(jgruber, v8:7449): The below is a hack to support tail-calls from
                // JS-linkage callers with a register code target. The problem is that the
                // code target register may be clobbered before the final jmp by
                // AssemblePopArgumentsAdaptorFrame. As a more permanent fix we could
                // entirely remove support for tail-calls from JS-linkage callers.
                buffer->instruction_args.push_back(
                    (call_code_immediate && callee->opcode() == IrOpcode::kHeapConstant)
                        ? g.UseImmediate(callee)
                        : call_use_fixed_target_reg
                            ? g.UseFixed(callee, kJavaScriptCallCodeStartRegister)
                            : is_tail_call ? g.UseUniqueRegister(callee)
                                           : call_through_slot ? g.UseUniqueSlot(callee)
                                                               : g.UseRegister(callee));
                break;
            case CallDescriptor::kCallAddress:
                buffer->instruction_args.push_back(
                    (call_address_immediate && callee->opcode() == IrOpcode::kExternalConstant)
                        ? g.UseImmediate(callee)
                        : call_use_fixed_target_reg
                            ? g.UseFixed(callee, kJavaScriptCallCodeStartRegister)
                            : g.UseRegister(callee));
                break;
            case CallDescriptor::kCallWasmFunction:
            case CallDescriptor::kCallWasmImportWrapper:
                buffer->instruction_args.push_back(
                    (call_address_immediate && (callee->opcode() == IrOpcode::kRelocatableInt64Constant || callee->opcode() == IrOpcode::kRelocatableInt32Constant))
                        ? g.UseImmediate(callee)
                        : call_use_fixed_target_reg
                            ? g.UseFixed(callee, kJavaScriptCallCodeStartRegister)
                            : g.UseRegister(callee));
                break;
            case CallDescriptor::kCallBuiltinPointer:
                // The common case for builtin pointers is to have the target in a
                // register. If we have a constant, we use a register anyway to simplify
                // related code.
                buffer->instruction_args.push_back(
                    call_use_fixed_target_reg
                        ? g.UseFixed(callee, kJavaScriptCallCodeStartRegister)
                        : g.UseRegister(callee));
                break;
            case CallDescriptor::kCallJSFunction:
                buffer->instruction_args.push_back(
                    g.UseLocation(callee, buffer->descriptor->GetInputLocation(0)));
                break;
            }
            DCHECK_EQ(1u, buffer->instruction_args.size());

            // Argument 1 is used for poison-alias index (encoded in a word-sized
            // immediate. This an index of the operand that aliases with poison register
            // or -1 if there is no aliasing.
            buffer->instruction_args.push_back(g.TempImmediate(-1));
            const size_t poison_alias_index = 1;
            DCHECK_EQ(buffer->instruction_args.size() - 1, poison_alias_index);

            // If the call needs a frame state, we insert the state information as
            // follows (n is the number of value inputs to the frame state):
            // arg 2               : deoptimization id.
            // arg 3 - arg (n + 2) : value inputs to the frame state.
            size_t frame_state_entries = 0;
            USE(frame_state_entries); // frame_state_entries is only used for debug.
            if (buffer->frame_state_descriptor != nullptr) {
                Node* frame_state = call->InputAt(static_cast<int>(buffer->descriptor->InputCount()));

                // If it was a syntactic tail call we need to drop the current frame and
                // all the frames on top of it that are either an arguments adaptor frame
                // or a tail caller frame.
                if (is_tail_call) {
                    frame_state = NodeProperties::GetFrameStateInput(frame_state);
                    buffer->frame_state_descriptor = buffer->frame_state_descriptor->outer_state();
                    while (buffer->frame_state_descriptor != nullptr && buffer->frame_state_descriptor->type() == FrameStateType::kArgumentsAdaptor) {
                        frame_state = NodeProperties::GetFrameStateInput(frame_state);
                        buffer->frame_state_descriptor = buffer->frame_state_descriptor->outer_state();
                    }
                }

                int const state_id = sequence()->AddDeoptimizationEntry(
                    buffer->frame_state_descriptor, DeoptimizeKind::kLazy,
                    DeoptimizeReason::kUnknown, VectorSlotPair());
                buffer->instruction_args.push_back(g.TempImmediate(state_id));

                StateObjectDeduplicator deduplicator(instruction_zone());

                frame_state_entries = 1 + AddInputsToFrameStateDescriptor(buffer->frame_state_descriptor, frame_state, &g, &deduplicator, &buffer->instruction_args, FrameStateInputKind::kStackSlot, instruction_zone());

                DCHECK_EQ(2 + frame_state_entries, buffer->instruction_args.size());
            }

            size_t input_count = static_cast<size_t>(buffer->input_count());

            // Split the arguments into pushed_nodes and instruction_args. Pushed
            // arguments require an explicit push instruction before the call and do
            // not appear as arguments to the call. Everything else ends up
            // as an InstructionOperand argument to the call.
            auto iter(call->inputs().begin());
            size_t pushed_count = 0;
            bool call_tail = (flags & kCallTail) != 0;
            for (size_t index = 0; index < input_count; ++iter, ++index) {
                DCHECK(iter != call->inputs().end());
                DCHECK_NE(IrOpcode::kFrameState, (*iter)->op()->opcode());
                if (index == 0)
                    continue; // The first argument (callee) is already done.

                LinkageLocation location = buffer->descriptor->GetInputLocation(index);
                if (call_tail) {
                    location = LinkageLocation::ConvertToTailCallerLocation(
                        location, stack_param_delta);
                }
                InstructionOperand op = g.UseLocation(*iter, location);
                UnallocatedOperand unallocated = UnallocatedOperand::cast(op);
                if (unallocated.HasFixedSlotPolicy() && !call_tail) {
                    int stack_index = -unallocated.fixed_slot_index() - 1;
                    if (static_cast<size_t>(stack_index) >= buffer->pushed_nodes.size()) {
                        buffer->pushed_nodes.resize(stack_index + 1);
                    }
                    PushParameter param = { *iter, location };
                    buffer->pushed_nodes[stack_index] = param;
                    pushed_count++;
                } else {
                    // If we do load poisoning and the linkage uses the poisoning register,
                    // then we request the input in memory location, and during code
                    // generation, we move the input to the register.
                    if (poisoning_level_ != PoisoningMitigationLevel::kDontPoison && unallocated.HasFixedRegisterPolicy()) {
                        int reg = unallocated.fixed_register_index();
                        if (Register::from_code(reg) == kSpeculationPoisonRegister) {
                            buffer->instruction_args[poison_alias_index] = g.TempImmediate(
                                static_cast<int32_t>(buffer->instruction_args.size()));
                            op = g.UseRegisterOrSlotOrConstant(*iter);
                        }
                    }
                    buffer->instruction_args.push_back(op);
                }
            }
            DCHECK_EQ(input_count, buffer->instruction_args.size() + pushed_count - frame_state_entries - 1);
            if (V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK && call_tail && stack_param_delta != 0) {
                // For tail calls that change the size of their parameter list and keep
                // their return address on the stack, move the return address to just above
                // the parameters.
                LinkageLocation saved_return_location = LinkageLocation::ForSavedCallerReturnAddress();
                InstructionOperand return_address = g.UsePointerLocation(LinkageLocation::ConvertToTailCallerLocation(
                                                                             saved_return_location, stack_param_delta),
                    saved_return_location);
                buffer->instruction_args.push_back(return_address);
            }
        }

        bool InstructionSelector::IsSourcePositionUsed(Node* node)
        {
            return (source_position_mode_ == kAllSourcePositions || node->opcode() == IrOpcode::kCall || node->opcode() == IrOpcode::kCallWithCallerSavedRegisters || node->opcode() == IrOpcode::kTrapIf || node->opcode() == IrOpcode::kTrapUnless || node->opcode() == IrOpcode::kProtectedLoad || node->opcode() == IrOpcode::kProtectedStore);
        }

        void InstructionSelector::VisitBlock(BasicBlock* block)
        {
            DCHECK(!current_block_);
            current_block_ = block;
            auto current_num_instructions = [&] {
                DCHECK_GE(kMaxInt, instructions_.size());
                return static_cast<int>(instructions_.size());
            };
            int current_block_end = current_num_instructions();

            int effect_level = 0;
            for (Node* const node : *block) {
                SetEffectLevel(node, effect_level);
                if (node->opcode() == IrOpcode::kStore || node->opcode() == IrOpcode::kUnalignedStore || node->opcode() == IrOpcode::kCall || node->opcode() == IrOpcode::kCallWithCallerSavedRegisters || node->opcode() == IrOpcode::kProtectedLoad || node->opcode() == IrOpcode::kProtectedStore) {
                    ++effect_level;
                }
            }

            // We visit the control first, then the nodes in the block, so the block's
            // control input should be on the same effect level as the last node.
            if (block->control_input() != nullptr) {
                SetEffectLevel(block->control_input(), effect_level);
            }

            auto FinishEmittedInstructions = [&](Node* node, int instruction_start) {
                if (instruction_selection_failed())
                    return false;
                if (current_num_instructions() == instruction_start)
                    return true;
                std::reverse(instructions_.begin() + instruction_start,
                    instructions_.end());
                if (!node)
                    return true;
                if (!source_positions_)
                    return true;
                SourcePosition source_position = source_positions_->GetSourcePosition(node);
                if (source_position.IsKnown() && IsSourcePositionUsed(node)) {
                    sequence()->SetSourcePosition(instructions_[instruction_start],
                        source_position);
                }
                return true;
            };

            // Generate code for the block control "top down", but schedule the code
            // "bottom up".
            VisitControl(block);
            if (!FinishEmittedInstructions(block->control_input(), current_block_end))
                return;

            // Visit code in reverse control flow order, because architecture-specific
            // matching may cover more than one node at a time.
            for (auto node : base::Reversed(*block)) {
                int current_node_end = current_num_instructions();
                // Skip nodes that are unused or already defined.
                if (IsUsed(node) && !IsDefined(node)) {
                    // Generate code for this node "top down", but schedule the code "bottom
                    // up".
                    VisitNode(node);
                    if (!FinishEmittedInstructions(node, current_node_end))
                        return;
                }
                if (trace_turbo_ == kEnableTraceTurboJson) {
                    instr_origins_[node->id()] = { current_num_instructions(),
                        current_node_end };
                }
            }

            // We're done with the block.
            InstructionBlock* instruction_block = sequence()->InstructionBlockAt(RpoNumber::FromInt(block->rpo_number()));
            if (current_num_instructions() == current_block_end) {
                // Avoid empty block: insert a {kArchNop} instruction.
                Emit(Instruction::New(sequence()->zone(), kArchNop));
            }
            instruction_block->set_code_start(current_num_instructions());
            instruction_block->set_code_end(current_block_end);
            current_block_ = nullptr;
        }

        void InstructionSelector::VisitControl(BasicBlock* block)
        {
#ifdef DEBUG
            // SSA deconstruction requires targets of branches not to have phis.
            // Edge split form guarantees this property, but is more strict.
            if (block->SuccessorCount() > 1) {
                for (BasicBlock* const successor : block->successors()) {
                    for (Node* const node : *successor) {
                        if (IrOpcode::IsPhiOpcode(node->opcode())) {
                            std::ostringstream str;
                            str << "You might have specified merged variables for a label with "
                                << "only one predecessor." << std::endl
                                << "# Current Block: " << *successor << std::endl
                                << "#          Node: " << *node;
                            FATAL("%s", str.str().c_str());
                        }
                    }
                }
            }
#endif

            Node* input = block->control_input();
            int instruction_end = static_cast<int>(instructions_.size());
            switch (block->control()) {
            case BasicBlock::kGoto:
                VisitGoto(block->SuccessorAt(0));
                break;
            case BasicBlock::kCall: {
                DCHECK_EQ(IrOpcode::kCall, input->opcode());
                BasicBlock* success = block->SuccessorAt(0);
                BasicBlock* exception = block->SuccessorAt(1);
                VisitCall(input, exception);
                VisitGoto(success);
                break;
            }
            case BasicBlock::kTailCall: {
                DCHECK_EQ(IrOpcode::kTailCall, input->opcode());
                VisitTailCall(input);
                break;
            }
            case BasicBlock::kBranch: {
                DCHECK_EQ(IrOpcode::kBranch, input->opcode());
                BasicBlock* tbranch = block->SuccessorAt(0);
                BasicBlock* fbranch = block->SuccessorAt(1);
                if (tbranch == fbranch) {
                    VisitGoto(tbranch);
                } else {
                    VisitBranch(input, tbranch, fbranch);
                }
                break;
            }
            case BasicBlock::kSwitch: {
                DCHECK_EQ(IrOpcode::kSwitch, input->opcode());
                // Last successor must be {IfDefault}.
                BasicBlock* default_branch = block->successors().back();
                DCHECK_EQ(IrOpcode::kIfDefault, default_branch->front()->opcode());
                // All other successors must be {IfValue}s.
                int32_t min_value = std::numeric_limits<int32_t>::max();
                int32_t max_value = std::numeric_limits<int32_t>::min();
                size_t case_count = block->SuccessorCount() - 1;
                ZoneVector<CaseInfo> cases(case_count, zone());
                for (size_t i = 0; i < case_count; ++i) {
                    BasicBlock* branch = block->SuccessorAt(i);
                    const IfValueParameters& p = IfValueParametersOf(branch->front()->op());
                    cases[i] = CaseInfo { p.value(), p.comparison_order(), branch };
                    if (min_value > p.value())
                        min_value = p.value();
                    if (max_value < p.value())
                        max_value = p.value();
                }
                SwitchInfo sw(cases, min_value, max_value, default_branch);
                VisitSwitch(input, sw);
                break;
            }
            case BasicBlock::kReturn: {
                DCHECK_EQ(IrOpcode::kReturn, input->opcode());
                VisitReturn(input);
                break;
            }
            case BasicBlock::kDeoptimize: {
                DeoptimizeParameters p = DeoptimizeParametersOf(input->op());
                Node* value = input->InputAt(0);
                VisitDeoptimize(p.kind(), p.reason(), p.feedback(), value);
                break;
            }
            case BasicBlock::kThrow:
                DCHECK_EQ(IrOpcode::kThrow, input->opcode());
                VisitThrow(input);
                break;
            case BasicBlock::kNone: {
                // Exit block doesn't have control.
                DCHECK_NULL(input);
                break;
            }
            default:
                UNREACHABLE();
                break;
            }
            if (trace_turbo_ == kEnableTraceTurboJson && input) {
                int instruction_start = static_cast<int>(instructions_.size());
                instr_origins_[input->id()] = { instruction_start, instruction_end };
            }
        }

        void InstructionSelector::MarkPairProjectionsAsWord32(Node* node)
        {
            Node* projection0 = NodeProperties::FindProjection(node, 0);
            if (projection0) {
                MarkAsWord32(projection0);
            }
            Node* projection1 = NodeProperties::FindProjection(node, 1);
            if (projection1) {
                MarkAsWord32(projection1);
            }
        }

        void InstructionSelector::VisitNode(Node* node)
        {
            DCHECK_NOT_NULL(schedule()->block(node)); // should only use scheduled nodes.
            switch (node->opcode()) {
            case IrOpcode::kStart:
            case IrOpcode::kLoop:
            case IrOpcode::kEnd:
            case IrOpcode::kBranch:
            case IrOpcode::kIfTrue:
            case IrOpcode::kIfFalse:
            case IrOpcode::kIfSuccess:
            case IrOpcode::kSwitch:
            case IrOpcode::kIfValue:
            case IrOpcode::kIfDefault:
            case IrOpcode::kEffectPhi:
            case IrOpcode::kMerge:
            case IrOpcode::kTerminate:
            case IrOpcode::kBeginRegion:
                // No code needed for these graph artifacts.
                return;
            case IrOpcode::kIfException:
                return MarkAsReference(node), VisitIfException(node);
            case IrOpcode::kFinishRegion:
                return MarkAsReference(node), VisitFinishRegion(node);
            case IrOpcode::kParameter: {
                MachineType type = linkage()->GetParameterType(ParameterIndexOf(node->op()));
                MarkAsRepresentation(type.representation(), node);
                return VisitParameter(node);
            }
            case IrOpcode::kOsrValue:
                return MarkAsReference(node), VisitOsrValue(node);
            case IrOpcode::kPhi: {
                MachineRepresentation rep = PhiRepresentationOf(node->op());
                if (rep == MachineRepresentation::kNone)
                    return;
                MarkAsRepresentation(rep, node);
                return VisitPhi(node);
            }
            case IrOpcode::kProjection:
                return VisitProjection(node);
            case IrOpcode::kInt32Constant:
            case IrOpcode::kInt64Constant:
            case IrOpcode::kExternalConstant:
            case IrOpcode::kRelocatableInt32Constant:
            case IrOpcode::kRelocatableInt64Constant:
                return VisitConstant(node);
            case IrOpcode::kFloat32Constant:
                return MarkAsFloat32(node), VisitConstant(node);
            case IrOpcode::kFloat64Constant:
                return MarkAsFloat64(node), VisitConstant(node);
            case IrOpcode::kHeapConstant:
                return MarkAsReference(node), VisitConstant(node);
            case IrOpcode::kNumberConstant: {
                double value = OpParameter<double>(node->op());
                if (!IsSmiDouble(value))
                    MarkAsReference(node);
                return VisitConstant(node);
            }
            case IrOpcode::kDelayedStringConstant:
                return MarkAsReference(node), VisitConstant(node);
            case IrOpcode::kCall:
                return VisitCall(node);
            case IrOpcode::kCallWithCallerSavedRegisters:
                return VisitCallWithCallerSavedRegisters(node);
            case IrOpcode::kDeoptimizeIf:
                return VisitDeoptimizeIf(node);
            case IrOpcode::kDeoptimizeUnless:
                return VisitDeoptimizeUnless(node);
            case IrOpcode::kTrapIf:
                return VisitTrapIf(node, TrapIdOf(node->op()));
            case IrOpcode::kTrapUnless:
                return VisitTrapUnless(node, TrapIdOf(node->op()));
            case IrOpcode::kFrameState:
            case IrOpcode::kStateValues:
            case IrOpcode::kObjectState:
                return;
            case IrOpcode::kDebugAbort:
                VisitDebugAbort(node);
                return;
            case IrOpcode::kDebugBreak:
                VisitDebugBreak(node);
                return;
            case IrOpcode::kUnreachable:
                VisitUnreachable(node);
                return;
            case IrOpcode::kDeadValue:
                VisitDeadValue(node);
                return;
            case IrOpcode::kComment:
                VisitComment(node);
                return;
            case IrOpcode::kRetain:
                VisitRetain(node);
                return;
            case IrOpcode::kLoad: {
                LoadRepresentation type = LoadRepresentationOf(node->op());
                MarkAsRepresentation(type.representation(), node);
                return VisitLoad(node);
            }
            case IrOpcode::kPoisonedLoad: {
                LoadRepresentation type = LoadRepresentationOf(node->op());
                MarkAsRepresentation(type.representation(), node);
                return VisitPoisonedLoad(node);
            }
            case IrOpcode::kStore:
                return VisitStore(node);
            case IrOpcode::kProtectedStore:
                return VisitProtectedStore(node);
            case IrOpcode::kWord32And:
                return MarkAsWord32(node), VisitWord32And(node);
            case IrOpcode::kWord32Or:
                return MarkAsWord32(node), VisitWord32Or(node);
            case IrOpcode::kWord32Xor:
                return MarkAsWord32(node), VisitWord32Xor(node);
            case IrOpcode::kWord32Shl:
                return MarkAsWord32(node), VisitWord32Shl(node);
            case IrOpcode::kWord32Shr:
                return MarkAsWord32(node), VisitWord32Shr(node);
            case IrOpcode::kWord32Sar:
                return MarkAsWord32(node), VisitWord32Sar(node);
            case IrOpcode::kWord32Ror:
                return MarkAsWord32(node), VisitWord32Ror(node);
            case IrOpcode::kWord32Equal:
                return VisitWord32Equal(node);
            case IrOpcode::kWord32Clz:
                return MarkAsWord32(node), VisitWord32Clz(node);
            case IrOpcode::kWord32Ctz:
                return MarkAsWord32(node), VisitWord32Ctz(node);
            case IrOpcode::kWord32ReverseBits:
                return MarkAsWord32(node), VisitWord32ReverseBits(node);
            case IrOpcode::kWord32ReverseBytes:
                return MarkAsWord32(node), VisitWord32ReverseBytes(node);
            case IrOpcode::kInt32AbsWithOverflow:
                return MarkAsWord32(node), VisitInt32AbsWithOverflow(node);
            case IrOpcode::kWord32Popcnt:
                return MarkAsWord32(node), VisitWord32Popcnt(node);
            case IrOpcode::kWord64Popcnt:
                return MarkAsWord32(node), VisitWord64Popcnt(node);
            case IrOpcode::kWord64And:
                return MarkAsWord64(node), VisitWord64And(node);
            case IrOpcode::kWord64Or:
                return MarkAsWord64(node), VisitWord64Or(node);
            case IrOpcode::kWord64Xor:
                return MarkAsWord64(node), VisitWord64Xor(node);
            case IrOpcode::kWord64Shl:
                return MarkAsWord64(node), VisitWord64Shl(node);
            case IrOpcode::kWord64Shr:
                return MarkAsWord64(node), VisitWord64Shr(node);
            case IrOpcode::kWord64Sar:
                return MarkAsWord64(node), VisitWord64Sar(node);
            case IrOpcode::kWord64Ror:
                return MarkAsWord64(node), VisitWord64Ror(node);
            case IrOpcode::kWord64Clz:
                return MarkAsWord64(node), VisitWord64Clz(node);
            case IrOpcode::kWord64Ctz:
                return MarkAsWord64(node), VisitWord64Ctz(node);
            case IrOpcode::kWord64ReverseBits:
                return MarkAsWord64(node), VisitWord64ReverseBits(node);
            case IrOpcode::kWord64ReverseBytes:
                return MarkAsWord64(node), VisitWord64ReverseBytes(node);
            case IrOpcode::kInt64AbsWithOverflow:
                return MarkAsWord64(node), VisitInt64AbsWithOverflow(node);
            case IrOpcode::kWord64Equal:
                return VisitWord64Equal(node);
            case IrOpcode::kInt32Add:
                return MarkAsWord32(node), VisitInt32Add(node);
            case IrOpcode::kInt32AddWithOverflow:
                return MarkAsWord32(node), VisitInt32AddWithOverflow(node);
            case IrOpcode::kInt32Sub:
                return MarkAsWord32(node), VisitInt32Sub(node);
            case IrOpcode::kInt32SubWithOverflow:
                return VisitInt32SubWithOverflow(node);
            case IrOpcode::kInt32Mul:
                return MarkAsWord32(node), VisitInt32Mul(node);
            case IrOpcode::kInt32MulWithOverflow:
                return MarkAsWord32(node), VisitInt32MulWithOverflow(node);
            case IrOpcode::kInt32MulHigh:
                return VisitInt32MulHigh(node);
            case IrOpcode::kInt32Div:
                return MarkAsWord32(node), VisitInt32Div(node);
            case IrOpcode::kInt32Mod:
                return MarkAsWord32(node), VisitInt32Mod(node);
            case IrOpcode::kInt32LessThan:
                return VisitInt32LessThan(node);
            case IrOpcode::kInt32LessThanOrEqual:
                return VisitInt32LessThanOrEqual(node);
            case IrOpcode::kUint32Div:
                return MarkAsWord32(node), VisitUint32Div(node);
            case IrOpcode::kUint32LessThan:
                return VisitUint32LessThan(node);
            case IrOpcode::kUint32LessThanOrEqual:
                return VisitUint32LessThanOrEqual(node);
            case IrOpcode::kUint32Mod:
                return MarkAsWord32(node), VisitUint32Mod(node);
            case IrOpcode::kUint32MulHigh:
                return VisitUint32MulHigh(node);
            case IrOpcode::kInt64Add:
                return MarkAsWord64(node), VisitInt64Add(node);
            case IrOpcode::kInt64AddWithOverflow:
                return MarkAsWord64(node), VisitInt64AddWithOverflow(node);
            case IrOpcode::kInt64Sub:
                return MarkAsWord64(node), VisitInt64Sub(node);
            case IrOpcode::kInt64SubWithOverflow:
                return MarkAsWord64(node), VisitInt64SubWithOverflow(node);
            case IrOpcode::kInt64Mul:
                return MarkAsWord64(node), VisitInt64Mul(node);
            case IrOpcode::kInt64Div:
                return MarkAsWord64(node), VisitInt64Div(node);
            case IrOpcode::kInt64Mod:
                return MarkAsWord64(node), VisitInt64Mod(node);
            case IrOpcode::kInt64LessThan:
                return VisitInt64LessThan(node);
            case IrOpcode::kInt64LessThanOrEqual:
                return VisitInt64LessThanOrEqual(node);
            case IrOpcode::kUint64Div:
                return MarkAsWord64(node), VisitUint64Div(node);
            case IrOpcode::kUint64LessThan:
                return VisitUint64LessThan(node);
            case IrOpcode::kUint64LessThanOrEqual:
                return VisitUint64LessThanOrEqual(node);
            case IrOpcode::kUint64Mod:
                return MarkAsWord64(node), VisitUint64Mod(node);
            case IrOpcode::kBitcastTaggedToWord:
                return MarkAsRepresentation(MachineType::PointerRepresentation(), node),
                       VisitBitcastTaggedToWord(node);
            case IrOpcode::kBitcastWordToTagged:
                return MarkAsReference(node), VisitBitcastWordToTagged(node);
            case IrOpcode::kBitcastWordToTaggedSigned:
                return MarkAsRepresentation(MachineRepresentation::kTaggedSigned, node),
                       EmitIdentity(node);
            case IrOpcode::kChangeFloat32ToFloat64:
                return MarkAsFloat64(node), VisitChangeFloat32ToFloat64(node);
            case IrOpcode::kChangeInt32ToFloat64:
                return MarkAsFloat64(node), VisitChangeInt32ToFloat64(node);
            case IrOpcode::kChangeInt64ToFloat64:
                return MarkAsFloat64(node), VisitChangeInt64ToFloat64(node);
            case IrOpcode::kChangeUint32ToFloat64:
                return MarkAsFloat64(node), VisitChangeUint32ToFloat64(node);
            case IrOpcode::kChangeFloat64ToInt32:
                return MarkAsWord32(node), VisitChangeFloat64ToInt32(node);
            case IrOpcode::kChangeFloat64ToInt64:
                return MarkAsWord64(node), VisitChangeFloat64ToInt64(node);
            case IrOpcode::kChangeFloat64ToUint32:
                return MarkAsWord32(node), VisitChangeFloat64ToUint32(node);
            case IrOpcode::kChangeFloat64ToUint64:
                return MarkAsWord64(node), VisitChangeFloat64ToUint64(node);
            case IrOpcode::kFloat64SilenceNaN:
                MarkAsFloat64(node);
                if (CanProduceSignalingNaN(node->InputAt(0))) {
                    return VisitFloat64SilenceNaN(node);
                } else {
                    return EmitIdentity(node);
                }
            case IrOpcode::kTruncateFloat64ToInt64:
                return MarkAsWord64(node), VisitTruncateFloat64ToInt64(node);
            case IrOpcode::kTruncateFloat64ToUint32:
                return MarkAsWord32(node), VisitTruncateFloat64ToUint32(node);
            case IrOpcode::kTruncateFloat32ToInt32:
                return MarkAsWord32(node), VisitTruncateFloat32ToInt32(node);
            case IrOpcode::kTruncateFloat32ToUint32:
                return MarkAsWord32(node), VisitTruncateFloat32ToUint32(node);
            case IrOpcode::kTryTruncateFloat32ToInt64:
                return MarkAsWord64(node), VisitTryTruncateFloat32ToInt64(node);
            case IrOpcode::kTryTruncateFloat64ToInt64:
                return MarkAsWord64(node), VisitTryTruncateFloat64ToInt64(node);
            case IrOpcode::kTryTruncateFloat32ToUint64:
                return MarkAsWord64(node), VisitTryTruncateFloat32ToUint64(node);
            case IrOpcode::kTryTruncateFloat64ToUint64:
                return MarkAsWord64(node), VisitTryTruncateFloat64ToUint64(node);
            case IrOpcode::kChangeInt32ToInt64:
                return MarkAsWord64(node), VisitChangeInt32ToInt64(node);
            case IrOpcode::kChangeUint32ToUint64:
                return MarkAsWord64(node), VisitChangeUint32ToUint64(node);
// TODO(mips-team): Support compress pointers.
#ifdef V8_COMPRESS_POINTERS
            case IrOpcode::kChangeTaggedToCompressed:
                return MarkAsCompressed(node), VisitChangeTaggedToCompressed(node);
            case IrOpcode::kChangeTaggedPointerToCompressedPointer:
                return MarkAsCompressed(node),
                       VisitChangeTaggedPointerToCompressedPointer(node);
            case IrOpcode::kChangeTaggedSignedToCompressedSigned:
                return MarkAsWord32(node),
                       VisitChangeTaggedSignedToCompressedSigned(node);
            case IrOpcode::kChangeCompressedToTagged:
                return MarkAsReference(node), VisitChangeCompressedToTagged(node);
            case IrOpcode::kChangeCompressedPointerToTaggedPointer:
                return MarkAsReference(node),
                       VisitChangeCompressedPointerToTaggedPointer(node);
            case IrOpcode::kChangeCompressedSignedToTaggedSigned:
                return MarkAsWord64(node),
                       VisitChangeCompressedSignedToTaggedSigned(node);
#endif
            case IrOpcode::kTruncateFloat64ToFloat32:
                return MarkAsFloat32(node), VisitTruncateFloat64ToFloat32(node);
            case IrOpcode::kTruncateFloat64ToWord32:
                return MarkAsWord32(node), VisitTruncateFloat64ToWord32(node);
            case IrOpcode::kTruncateInt64ToInt32:
                return MarkAsWord32(node), VisitTruncateInt64ToInt32(node);
            case IrOpcode::kRoundFloat64ToInt32:
                return MarkAsWord32(node), VisitRoundFloat64ToInt32(node);
            case IrOpcode::kRoundInt64ToFloat32:
                return MarkAsFloat32(node), VisitRoundInt64ToFloat32(node);
            case IrOpcode::kRoundInt32ToFloat32:
                return MarkAsFloat32(node), VisitRoundInt32ToFloat32(node);
            case IrOpcode::kRoundInt64ToFloat64:
                return MarkAsFloat64(node), VisitRoundInt64ToFloat64(node);
            case IrOpcode::kBitcastFloat32ToInt32:
                return MarkAsWord32(node), VisitBitcastFloat32ToInt32(node);
            case IrOpcode::kRoundUint32ToFloat32:
                return MarkAsFloat32(node), VisitRoundUint32ToFloat32(node);
            case IrOpcode::kRoundUint64ToFloat32:
                return MarkAsFloat64(node), VisitRoundUint64ToFloat32(node);
            case IrOpcode::kRoundUint64ToFloat64:
                return MarkAsFloat64(node), VisitRoundUint64ToFloat64(node);
            case IrOpcode::kBitcastFloat64ToInt64:
                return MarkAsWord64(node), VisitBitcastFloat64ToInt64(node);
            case IrOpcode::kBitcastInt32ToFloat32:
                return MarkAsFloat32(node), VisitBitcastInt32ToFloat32(node);
            case IrOpcode::kBitcastInt64ToFloat64:
                return MarkAsFloat64(node), VisitBitcastInt64ToFloat64(node);
            case IrOpcode::kFloat32Add:
                return MarkAsFloat32(node), VisitFloat32Add(node);
            case IrOpcode::kFloat32Sub:
                return MarkAsFloat32(node), VisitFloat32Sub(node);
            case IrOpcode::kFloat32Neg:
                return MarkAsFloat32(node), VisitFloat32Neg(node);
            case IrOpcode::kFloat32Mul:
                return MarkAsFloat32(node), VisitFloat32Mul(node);
            case IrOpcode::kFloat32Div:
                return MarkAsFloat32(node), VisitFloat32Div(node);
            case IrOpcode::kFloat32Abs:
                return MarkAsFloat32(node), VisitFloat32Abs(node);
            case IrOpcode::kFloat32Sqrt:
                return MarkAsFloat32(node), VisitFloat32Sqrt(node);
            case IrOpcode::kFloat32Equal:
                return VisitFloat32Equal(node);
            case IrOpcode::kFloat32LessThan:
                return VisitFloat32LessThan(node);
            case IrOpcode::kFloat32LessThanOrEqual:
                return VisitFloat32LessThanOrEqual(node);
            case IrOpcode::kFloat32Max:
                return MarkAsFloat32(node), VisitFloat32Max(node);
            case IrOpcode::kFloat32Min:
                return MarkAsFloat32(node), VisitFloat32Min(node);
            case IrOpcode::kFloat64Add:
                return MarkAsFloat64(node), VisitFloat64Add(node);
            case IrOpcode::kFloat64Sub:
                return MarkAsFloat64(node), VisitFloat64Sub(node);
            case IrOpcode::kFloat64Neg:
                return MarkAsFloat64(node), VisitFloat64Neg(node);
            case IrOpcode::kFloat64Mul:
                return MarkAsFloat64(node), VisitFloat64Mul(node);
            case IrOpcode::kFloat64Div:
                return MarkAsFloat64(node), VisitFloat64Div(node);
            case IrOpcode::kFloat64Mod:
                return MarkAsFloat64(node), VisitFloat64Mod(node);
            case IrOpcode::kFloat64Min:
                return MarkAsFloat64(node), VisitFloat64Min(node);
            case IrOpcode::kFloat64Max:
                return MarkAsFloat64(node), VisitFloat64Max(node);
            case IrOpcode::kFloat64Abs:
                return MarkAsFloat64(node), VisitFloat64Abs(node);
            case IrOpcode::kFloat64Acos:
                return MarkAsFloat64(node), VisitFloat64Acos(node);
            case IrOpcode::kFloat64Acosh:
                return MarkAsFloat64(node), VisitFloat64Acosh(node);
            case IrOpcode::kFloat64Asin:
                return MarkAsFloat64(node), VisitFloat64Asin(node);
            case IrOpcode::kFloat64Asinh:
                return MarkAsFloat64(node), VisitFloat64Asinh(node);
            case IrOpcode::kFloat64Atan:
                return MarkAsFloat64(node), VisitFloat64Atan(node);
            case IrOpcode::kFloat64Atanh:
                return MarkAsFloat64(node), VisitFloat64Atanh(node);
            case IrOpcode::kFloat64Atan2:
                return MarkAsFloat64(node), VisitFloat64Atan2(node);
            case IrOpcode::kFloat64Cbrt:
                return MarkAsFloat64(node), VisitFloat64Cbrt(node);
            case IrOpcode::kFloat64Cos:
                return MarkAsFloat64(node), VisitFloat64Cos(node);
            case IrOpcode::kFloat64Cosh:
                return MarkAsFloat64(node), VisitFloat64Cosh(node);
            case IrOpcode::kFloat64Exp:
                return MarkAsFloat64(node), VisitFloat64Exp(node);
            case IrOpcode::kFloat64Expm1:
                return MarkAsFloat64(node), VisitFloat64Expm1(node);
            case IrOpcode::kFloat64Log:
                return MarkAsFloat64(node), VisitFloat64Log(node);
            case IrOpcode::kFloat64Log1p:
                return MarkAsFloat64(node), VisitFloat64Log1p(node);
            case IrOpcode::kFloat64Log10:
                return MarkAsFloat64(node), VisitFloat64Log10(node);
            case IrOpcode::kFloat64Log2:
                return MarkAsFloat64(node), VisitFloat64Log2(node);
            case IrOpcode::kFloat64Pow:
                return MarkAsFloat64(node), VisitFloat64Pow(node);
            case IrOpcode::kFloat64Sin:
                return MarkAsFloat64(node), VisitFloat64Sin(node);
            case IrOpcode::kFloat64Sinh:
                return MarkAsFloat64(node), VisitFloat64Sinh(node);
            case IrOpcode::kFloat64Sqrt:
                return MarkAsFloat64(node), VisitFloat64Sqrt(node);
            case IrOpcode::kFloat64Tan:
                return MarkAsFloat64(node), VisitFloat64Tan(node);
            case IrOpcode::kFloat64Tanh:
                return MarkAsFloat64(node), VisitFloat64Tanh(node);
            case IrOpcode::kFloat64Equal:
                return VisitFloat64Equal(node);
            case IrOpcode::kFloat64LessThan:
                return VisitFloat64LessThan(node);
            case IrOpcode::kFloat64LessThanOrEqual:
                return VisitFloat64LessThanOrEqual(node);
            case IrOpcode::kFloat32RoundDown:
                return MarkAsFloat32(node), VisitFloat32RoundDown(node);
            case IrOpcode::kFloat64RoundDown:
                return MarkAsFloat64(node), VisitFloat64RoundDown(node);
            case IrOpcode::kFloat32RoundUp:
                return MarkAsFloat32(node), VisitFloat32RoundUp(node);
            case IrOpcode::kFloat64RoundUp:
                return MarkAsFloat64(node), VisitFloat64RoundUp(node);
            case IrOpcode::kFloat32RoundTruncate:
                return MarkAsFloat32(node), VisitFloat32RoundTruncate(node);
            case IrOpcode::kFloat64RoundTruncate:
                return MarkAsFloat64(node), VisitFloat64RoundTruncate(node);
            case IrOpcode::kFloat64RoundTiesAway:
                return MarkAsFloat64(node), VisitFloat64RoundTiesAway(node);
            case IrOpcode::kFloat32RoundTiesEven:
                return MarkAsFloat32(node), VisitFloat32RoundTiesEven(node);
            case IrOpcode::kFloat64RoundTiesEven:
                return MarkAsFloat64(node), VisitFloat64RoundTiesEven(node);
            case IrOpcode::kFloat64ExtractLowWord32:
                return MarkAsWord32(node), VisitFloat64ExtractLowWord32(node);
            case IrOpcode::kFloat64ExtractHighWord32:
                return MarkAsWord32(node), VisitFloat64ExtractHighWord32(node);
            case IrOpcode::kFloat64InsertLowWord32:
                return MarkAsFloat64(node), VisitFloat64InsertLowWord32(node);
            case IrOpcode::kFloat64InsertHighWord32:
                return MarkAsFloat64(node), VisitFloat64InsertHighWord32(node);
            case IrOpcode::kTaggedPoisonOnSpeculation:
                return MarkAsReference(node), VisitTaggedPoisonOnSpeculation(node);
            case IrOpcode::kWord32PoisonOnSpeculation:
                return MarkAsWord32(node), VisitWord32PoisonOnSpeculation(node);
            case IrOpcode::kWord64PoisonOnSpeculation:
                return MarkAsWord64(node), VisitWord64PoisonOnSpeculation(node);
            case IrOpcode::kStackSlot:
                return VisitStackSlot(node);
            case IrOpcode::kLoadStackPointer:
                return VisitLoadStackPointer(node);
            case IrOpcode::kLoadFramePointer:
                return VisitLoadFramePointer(node);
            case IrOpcode::kLoadParentFramePointer:
                return VisitLoadParentFramePointer(node);
            case IrOpcode::kUnalignedLoad: {
                LoadRepresentation type = LoadRepresentationOf(node->op());
                MarkAsRepresentation(type.representation(), node);
                return VisitUnalignedLoad(node);
            }
            case IrOpcode::kUnalignedStore:
                return VisitUnalignedStore(node);
            case IrOpcode::kInt32PairAdd:
                MarkAsWord32(node);
                MarkPairProjectionsAsWord32(node);
                return VisitInt32PairAdd(node);
            case IrOpcode::kInt32PairSub:
                MarkAsWord32(node);
                MarkPairProjectionsAsWord32(node);
                return VisitInt32PairSub(node);
            case IrOpcode::kInt32PairMul:
                MarkAsWord32(node);
                MarkPairProjectionsAsWord32(node);
                return VisitInt32PairMul(node);
            case IrOpcode::kWord32PairShl:
                MarkAsWord32(node);
                MarkPairProjectionsAsWord32(node);
                return VisitWord32PairShl(node);
            case IrOpcode::kWord32PairShr:
                MarkAsWord32(node);
                MarkPairProjectionsAsWord32(node);
                return VisitWord32PairShr(node);
            case IrOpcode::kWord32PairSar:
                MarkAsWord32(node);
                MarkPairProjectionsAsWord32(node);
                return VisitWord32PairSar(node);
            case IrOpcode::kWord32AtomicLoad: {
                LoadRepresentation type = LoadRepresentationOf(node->op());
                MarkAsRepresentation(type.representation(), node);
                return VisitWord32AtomicLoad(node);
            }
            case IrOpcode::kWord64AtomicLoad: {
                LoadRepresentation type = LoadRepresentationOf(node->op());
                MarkAsRepresentation(type.representation(), node);
                return VisitWord64AtomicLoad(node);
            }
            case IrOpcode::kWord32AtomicStore:
                return VisitWord32AtomicStore(node);
            case IrOpcode::kWord64AtomicStore:
                return VisitWord64AtomicStore(node);
            case IrOpcode::kWord32AtomicPairStore:
                return VisitWord32AtomicPairStore(node);
            case IrOpcode::kWord32AtomicPairLoad: {
                MarkAsWord32(node);
                MarkPairProjectionsAsWord32(node);
                return VisitWord32AtomicPairLoad(node);
            }
#define ATOMIC_CASE(name, rep)                             \
    case IrOpcode::k##rep##Atomic##name: {                 \
        MachineType type = AtomicOpType(node->op());       \
        MarkAsRepresentation(type.representation(), node); \
        return Visit##rep##Atomic##name(node);             \
    }
                ATOMIC_CASE(Add, Word32)
                ATOMIC_CASE(Add, Word64)
                ATOMIC_CASE(Sub, Word32)
                ATOMIC_CASE(Sub, Word64)
                ATOMIC_CASE(And, Word32)
                ATOMIC_CASE(And, Word64)
                ATOMIC_CASE(Or, Word32)
                ATOMIC_CASE(Or, Word64)
                ATOMIC_CASE(Xor, Word32)
                ATOMIC_CASE(Xor, Word64)
                ATOMIC_CASE(Exchange, Word32)
                ATOMIC_CASE(Exchange, Word64)
                ATOMIC_CASE(CompareExchange, Word32)
                ATOMIC_CASE(CompareExchange, Word64)
#undef ATOMIC_CASE
#define ATOMIC_CASE(name)                         \
    case IrOpcode::kWord32AtomicPair##name: {     \
        MarkAsWord32(node);                       \
        MarkPairProjectionsAsWord32(node);        \
        return VisitWord32AtomicPair##name(node); \
    }
                ATOMIC_CASE(Add)
                ATOMIC_CASE(Sub)
                ATOMIC_CASE(And)
                ATOMIC_CASE(Or)
                ATOMIC_CASE(Xor)
                ATOMIC_CASE(Exchange)
                ATOMIC_CASE(CompareExchange)
#undef ATOMIC_CASE
            case IrOpcode::kProtectedLoad: {
                LoadRepresentation type = LoadRepresentationOf(node->op());
                MarkAsRepresentation(type.representation(), node);
                return VisitProtectedLoad(node);
            }
            case IrOpcode::kSignExtendWord8ToInt32:
                return MarkAsWord32(node), VisitSignExtendWord8ToInt32(node);
            case IrOpcode::kSignExtendWord16ToInt32:
                return MarkAsWord32(node), VisitSignExtendWord16ToInt32(node);
            case IrOpcode::kSignExtendWord8ToInt64:
                return MarkAsWord64(node), VisitSignExtendWord8ToInt64(node);
            case IrOpcode::kSignExtendWord16ToInt64:
                return MarkAsWord64(node), VisitSignExtendWord16ToInt64(node);
            case IrOpcode::kSignExtendWord32ToInt64:
                return MarkAsWord64(node), VisitSignExtendWord32ToInt64(node);
            case IrOpcode::kUnsafePointerAdd:
                MarkAsRepresentation(MachineType::PointerRepresentation(), node);
                return VisitUnsafePointerAdd(node);
            case IrOpcode::kF32x4Splat:
                return MarkAsSimd128(node), VisitF32x4Splat(node);
            case IrOpcode::kF32x4ExtractLane:
                return MarkAsFloat32(node), VisitF32x4ExtractLane(node);
            case IrOpcode::kF32x4ReplaceLane:
                return MarkAsSimd128(node), VisitF32x4ReplaceLane(node);
            case IrOpcode::kF32x4SConvertI32x4:
                return MarkAsSimd128(node), VisitF32x4SConvertI32x4(node);
            case IrOpcode::kF32x4UConvertI32x4:
                return MarkAsSimd128(node), VisitF32x4UConvertI32x4(node);
            case IrOpcode::kF32x4Abs:
                return MarkAsSimd128(node), VisitF32x4Abs(node);
            case IrOpcode::kF32x4Neg:
                return MarkAsSimd128(node), VisitF32x4Neg(node);
            case IrOpcode::kF32x4RecipApprox:
                return MarkAsSimd128(node), VisitF32x4RecipApprox(node);
            case IrOpcode::kF32x4RecipSqrtApprox:
                return MarkAsSimd128(node), VisitF32x4RecipSqrtApprox(node);
            case IrOpcode::kF32x4Add:
                return MarkAsSimd128(node), VisitF32x4Add(node);
            case IrOpcode::kF32x4AddHoriz:
                return MarkAsSimd128(node), VisitF32x4AddHoriz(node);
            case IrOpcode::kF32x4Sub:
                return MarkAsSimd128(node), VisitF32x4Sub(node);
            case IrOpcode::kF32x4Mul:
                return MarkAsSimd128(node), VisitF32x4Mul(node);
            case IrOpcode::kF32x4Min:
                return MarkAsSimd128(node), VisitF32x4Min(node);
            case IrOpcode::kF32x4Max:
                return MarkAsSimd128(node), VisitF32x4Max(node);
            case IrOpcode::kF32x4Eq:
                return MarkAsSimd128(node), VisitF32x4Eq(node);
            case IrOpcode::kF32x4Ne:
                return MarkAsSimd128(node), VisitF32x4Ne(node);
            case IrOpcode::kF32x4Lt:
                return MarkAsSimd128(node), VisitF32x4Lt(node);
            case IrOpcode::kF32x4Le:
                return MarkAsSimd128(node), VisitF32x4Le(node);
            case IrOpcode::kI32x4Splat:
                return MarkAsSimd128(node), VisitI32x4Splat(node);
            case IrOpcode::kI32x4ExtractLane:
                return MarkAsWord32(node), VisitI32x4ExtractLane(node);
            case IrOpcode::kI32x4ReplaceLane:
                return MarkAsSimd128(node), VisitI32x4ReplaceLane(node);
            case IrOpcode::kI32x4SConvertF32x4:
                return MarkAsSimd128(node), VisitI32x4SConvertF32x4(node);
            case IrOpcode::kI32x4SConvertI16x8Low:
                return MarkAsSimd128(node), VisitI32x4SConvertI16x8Low(node);
            case IrOpcode::kI32x4SConvertI16x8High:
                return MarkAsSimd128(node), VisitI32x4SConvertI16x8High(node);
            case IrOpcode::kI32x4Neg:
                return MarkAsSimd128(node), VisitI32x4Neg(node);
            case IrOpcode::kI32x4Shl:
                return MarkAsSimd128(node), VisitI32x4Shl(node);
            case IrOpcode::kI32x4ShrS:
                return MarkAsSimd128(node), VisitI32x4ShrS(node);
            case IrOpcode::kI32x4Add:
                return MarkAsSimd128(node), VisitI32x4Add(node);
            case IrOpcode::kI32x4AddHoriz:
                return MarkAsSimd128(node), VisitI32x4AddHoriz(node);
            case IrOpcode::kI32x4Sub:
                return MarkAsSimd128(node), VisitI32x4Sub(node);
            case IrOpcode::kI32x4Mul:
                return MarkAsSimd128(node), VisitI32x4Mul(node);
            case IrOpcode::kI32x4MinS:
                return MarkAsSimd128(node), VisitI32x4MinS(node);
            case IrOpcode::kI32x4MaxS:
                return MarkAsSimd128(node), VisitI32x4MaxS(node);
            case IrOpcode::kI32x4Eq:
                return MarkAsSimd128(node), VisitI32x4Eq(node);
            case IrOpcode::kI32x4Ne:
                return MarkAsSimd128(node), VisitI32x4Ne(node);
            case IrOpcode::kI32x4GtS:
                return MarkAsSimd128(node), VisitI32x4GtS(node);
            case IrOpcode::kI32x4GeS:
                return MarkAsSimd128(node), VisitI32x4GeS(node);
            case IrOpcode::kI32x4UConvertF32x4:
                return MarkAsSimd128(node), VisitI32x4UConvertF32x4(node);
            case IrOpcode::kI32x4UConvertI16x8Low:
                return MarkAsSimd128(node), VisitI32x4UConvertI16x8Low(node);
            case IrOpcode::kI32x4UConvertI16x8High:
                return MarkAsSimd128(node), VisitI32x4UConvertI16x8High(node);
            case IrOpcode::kI32x4ShrU:
                return MarkAsSimd128(node), VisitI32x4ShrU(node);
            case IrOpcode::kI32x4MinU:
                return MarkAsSimd128(node), VisitI32x4MinU(node);
            case IrOpcode::kI32x4MaxU:
                return MarkAsSimd128(node), VisitI32x4MaxU(node);
            case IrOpcode::kI32x4GtU:
                return MarkAsSimd128(node), VisitI32x4GtU(node);
            case IrOpcode::kI32x4GeU:
                return MarkAsSimd128(node), VisitI32x4GeU(node);
            case IrOpcode::kI16x8Splat:
                return MarkAsSimd128(node), VisitI16x8Splat(node);
            case IrOpcode::kI16x8ExtractLane:
                return MarkAsWord32(node), VisitI16x8ExtractLane(node);
            case IrOpcode::kI16x8ReplaceLane:
                return MarkAsSimd128(node), VisitI16x8ReplaceLane(node);
            case IrOpcode::kI16x8SConvertI8x16Low:
                return MarkAsSimd128(node), VisitI16x8SConvertI8x16Low(node);
            case IrOpcode::kI16x8SConvertI8x16High:
                return MarkAsSimd128(node), VisitI16x8SConvertI8x16High(node);
            case IrOpcode::kI16x8Neg:
                return MarkAsSimd128(node), VisitI16x8Neg(node);
            case IrOpcode::kI16x8Shl:
                return MarkAsSimd128(node), VisitI16x8Shl(node);
            case IrOpcode::kI16x8ShrS:
                return MarkAsSimd128(node), VisitI16x8ShrS(node);
            case IrOpcode::kI16x8SConvertI32x4:
                return MarkAsSimd128(node), VisitI16x8SConvertI32x4(node);
            case IrOpcode::kI16x8Add:
                return MarkAsSimd128(node), VisitI16x8Add(node);
            case IrOpcode::kI16x8AddSaturateS:
                return MarkAsSimd128(node), VisitI16x8AddSaturateS(node);
            case IrOpcode::kI16x8AddHoriz:
                return MarkAsSimd128(node), VisitI16x8AddHoriz(node);
            case IrOpcode::kI16x8Sub:
                return MarkAsSimd128(node), VisitI16x8Sub(node);
            case IrOpcode::kI16x8SubSaturateS:
                return MarkAsSimd128(node), VisitI16x8SubSaturateS(node);
            case IrOpcode::kI16x8Mul:
                return MarkAsSimd128(node), VisitI16x8Mul(node);
            case IrOpcode::kI16x8MinS:
                return MarkAsSimd128(node), VisitI16x8MinS(node);
            case IrOpcode::kI16x8MaxS:
                return MarkAsSimd128(node), VisitI16x8MaxS(node);
            case IrOpcode::kI16x8Eq:
                return MarkAsSimd128(node), VisitI16x8Eq(node);
            case IrOpcode::kI16x8Ne:
                return MarkAsSimd128(node), VisitI16x8Ne(node);
            case IrOpcode::kI16x8GtS:
                return MarkAsSimd128(node), VisitI16x8GtS(node);
            case IrOpcode::kI16x8GeS:
                return MarkAsSimd128(node), VisitI16x8GeS(node);
            case IrOpcode::kI16x8UConvertI8x16Low:
                return MarkAsSimd128(node), VisitI16x8UConvertI8x16Low(node);
            case IrOpcode::kI16x8UConvertI8x16High:
                return MarkAsSimd128(node), VisitI16x8UConvertI8x16High(node);
            case IrOpcode::kI16x8ShrU:
                return MarkAsSimd128(node), VisitI16x8ShrU(node);
            case IrOpcode::kI16x8UConvertI32x4:
                return MarkAsSimd128(node), VisitI16x8UConvertI32x4(node);
            case IrOpcode::kI16x8AddSaturateU:
                return MarkAsSimd128(node), VisitI16x8AddSaturateU(node);
            case IrOpcode::kI16x8SubSaturateU:
                return MarkAsSimd128(node), VisitI16x8SubSaturateU(node);
            case IrOpcode::kI16x8MinU:
                return MarkAsSimd128(node), VisitI16x8MinU(node);
            case IrOpcode::kI16x8MaxU:
                return MarkAsSimd128(node), VisitI16x8MaxU(node);
            case IrOpcode::kI16x8GtU:
                return MarkAsSimd128(node), VisitI16x8GtU(node);
            case IrOpcode::kI16x8GeU:
                return MarkAsSimd128(node), VisitI16x8GeU(node);
            case IrOpcode::kI8x16Splat:
                return MarkAsSimd128(node), VisitI8x16Splat(node);
            case IrOpcode::kI8x16ExtractLane:
                return MarkAsWord32(node), VisitI8x16ExtractLane(node);
            case IrOpcode::kI8x16ReplaceLane:
                return MarkAsSimd128(node), VisitI8x16ReplaceLane(node);
            case IrOpcode::kI8x16Neg:
                return MarkAsSimd128(node), VisitI8x16Neg(node);
            case IrOpcode::kI8x16Shl:
                return MarkAsSimd128(node), VisitI8x16Shl(node);
            case IrOpcode::kI8x16ShrS:
                return MarkAsSimd128(node), VisitI8x16ShrS(node);
            case IrOpcode::kI8x16SConvertI16x8:
                return MarkAsSimd128(node), VisitI8x16SConvertI16x8(node);
            case IrOpcode::kI8x16Add:
                return MarkAsSimd128(node), VisitI8x16Add(node);
            case IrOpcode::kI8x16AddSaturateS:
                return MarkAsSimd128(node), VisitI8x16AddSaturateS(node);
            case IrOpcode::kI8x16Sub:
                return MarkAsSimd128(node), VisitI8x16Sub(node);
            case IrOpcode::kI8x16SubSaturateS:
                return MarkAsSimd128(node), VisitI8x16SubSaturateS(node);
            case IrOpcode::kI8x16Mul:
                return MarkAsSimd128(node), VisitI8x16Mul(node);
            case IrOpcode::kI8x16MinS:
                return MarkAsSimd128(node), VisitI8x16MinS(node);
            case IrOpcode::kI8x16MaxS:
                return MarkAsSimd128(node), VisitI8x16MaxS(node);
            case IrOpcode::kI8x16Eq:
                return MarkAsSimd128(node), VisitI8x16Eq(node);
            case IrOpcode::kI8x16Ne:
                return MarkAsSimd128(node), VisitI8x16Ne(node);
            case IrOpcode::kI8x16GtS:
                return MarkAsSimd128(node), VisitI8x16GtS(node);
            case IrOpcode::kI8x16GeS:
                return MarkAsSimd128(node), VisitI8x16GeS(node);
            case IrOpcode::kI8x16ShrU:
                return MarkAsSimd128(node), VisitI8x16ShrU(node);
            case IrOpcode::kI8x16UConvertI16x8:
                return MarkAsSimd128(node), VisitI8x16UConvertI16x8(node);
            case IrOpcode::kI8x16AddSaturateU:
                return MarkAsSimd128(node), VisitI8x16AddSaturateU(node);
            case IrOpcode::kI8x16SubSaturateU:
                return MarkAsSimd128(node), VisitI8x16SubSaturateU(node);
            case IrOpcode::kI8x16MinU:
                return MarkAsSimd128(node), VisitI8x16MinU(node);
            case IrOpcode::kI8x16MaxU:
                return MarkAsSimd128(node), VisitI8x16MaxU(node);
            case IrOpcode::kI8x16GtU:
                return MarkAsSimd128(node), VisitI8x16GtU(node);
            case IrOpcode::kI8x16GeU:
                return MarkAsSimd128(node), VisitI8x16GeU(node);
            case IrOpcode::kS128Zero:
                return MarkAsSimd128(node), VisitS128Zero(node);
            case IrOpcode::kS128And:
                return MarkAsSimd128(node), VisitS128And(node);
            case IrOpcode::kS128Or:
                return MarkAsSimd128(node), VisitS128Or(node);
            case IrOpcode::kS128Xor:
                return MarkAsSimd128(node), VisitS128Xor(node);
            case IrOpcode::kS128Not:
                return MarkAsSimd128(node), VisitS128Not(node);
            case IrOpcode::kS128Select:
                return MarkAsSimd128(node), VisitS128Select(node);
            case IrOpcode::kS8x16Shuffle:
                return MarkAsSimd128(node), VisitS8x16Shuffle(node);
            case IrOpcode::kS1x4AnyTrue:
                return MarkAsWord32(node), VisitS1x4AnyTrue(node);
            case IrOpcode::kS1x4AllTrue:
                return MarkAsWord32(node), VisitS1x4AllTrue(node);
            case IrOpcode::kS1x8AnyTrue:
                return MarkAsWord32(node), VisitS1x8AnyTrue(node);
            case IrOpcode::kS1x8AllTrue:
                return MarkAsWord32(node), VisitS1x8AllTrue(node);
            case IrOpcode::kS1x16AnyTrue:
                return MarkAsWord32(node), VisitS1x16AnyTrue(node);
            case IrOpcode::kS1x16AllTrue:
                return MarkAsWord32(node), VisitS1x16AllTrue(node);
            default:
                FATAL("Unexpected operator #%d:%s @ node #%d", node->opcode(),
                    node->op()->mnemonic(), node->id());
                break;
            }
        }

        void InstructionSelector::EmitWordPoisonOnSpeculation(Node* node)
        {
            if (poisoning_level_ != PoisoningMitigationLevel::kDontPoison) {
                OperandGenerator g(this);
                Node* input_node = NodeProperties::GetValueInput(node, 0);
                InstructionOperand input = g.UseRegister(input_node);
                InstructionOperand output = g.DefineSameAsFirst(node);
                Emit(kArchWordPoisonOnSpeculation, output, input);
            } else {
                EmitIdentity(node);
            }
        }

        void InstructionSelector::VisitWord32PoisonOnSpeculation(Node* node)
        {
            EmitWordPoisonOnSpeculation(node);
        }

        void InstructionSelector::VisitWord64PoisonOnSpeculation(Node* node)
        {
            EmitWordPoisonOnSpeculation(node);
        }

        void InstructionSelector::VisitTaggedPoisonOnSpeculation(Node* node)
        {
            EmitWordPoisonOnSpeculation(node);
        }

        void InstructionSelector::VisitLoadStackPointer(Node* node)
        {
            OperandGenerator g(this);
            Emit(kArchStackPointer, g.DefineAsRegister(node));
        }

        void InstructionSelector::VisitLoadFramePointer(Node* node)
        {
            OperandGenerator g(this);
            Emit(kArchFramePointer, g.DefineAsRegister(node));
        }

        void InstructionSelector::VisitLoadParentFramePointer(Node* node)
        {
            OperandGenerator g(this);
            Emit(kArchParentFramePointer, g.DefineAsRegister(node));
        }

        void InstructionSelector::VisitFloat64Acos(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Acos);
        }

        void InstructionSelector::VisitFloat64Acosh(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Acosh);
        }

        void InstructionSelector::VisitFloat64Asin(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Asin);
        }

        void InstructionSelector::VisitFloat64Asinh(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Asinh);
        }

        void InstructionSelector::VisitFloat64Atan(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Atan);
        }

        void InstructionSelector::VisitFloat64Atanh(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Atanh);
        }

        void InstructionSelector::VisitFloat64Atan2(Node* node)
        {
            VisitFloat64Ieee754Binop(node, kIeee754Float64Atan2);
        }

        void InstructionSelector::VisitFloat64Cbrt(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Cbrt);
        }

        void InstructionSelector::VisitFloat64Cos(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Cos);
        }

        void InstructionSelector::VisitFloat64Cosh(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Cosh);
        }

        void InstructionSelector::VisitFloat64Exp(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Exp);
        }

        void InstructionSelector::VisitFloat64Expm1(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Expm1);
        }

        void InstructionSelector::VisitFloat64Log(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Log);
        }

        void InstructionSelector::VisitFloat64Log1p(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Log1p);
        }

        void InstructionSelector::VisitFloat64Log2(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Log2);
        }

        void InstructionSelector::VisitFloat64Log10(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Log10);
        }

        void InstructionSelector::VisitFloat64Pow(Node* node)
        {
            VisitFloat64Ieee754Binop(node, kIeee754Float64Pow);
        }

        void InstructionSelector::VisitFloat64Sin(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Sin);
        }

        void InstructionSelector::VisitFloat64Sinh(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Sinh);
        }

        void InstructionSelector::VisitFloat64Tan(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Tan);
        }

        void InstructionSelector::VisitFloat64Tanh(Node* node)
        {
            VisitFloat64Ieee754Unop(node, kIeee754Float64Tanh);
        }

        void InstructionSelector::EmitTableSwitch(const SwitchInfo& sw,
            InstructionOperand& index_operand)
        {
            OperandGenerator g(this);
            size_t input_count = 2 + sw.value_range();
            DCHECK_LE(sw.value_range(), std::numeric_limits<size_t>::max() - 2);
            auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
            inputs[0] = index_operand;
            InstructionOperand default_operand = g.Label(sw.default_branch());
            std::fill(&inputs[1], &inputs[input_count], default_operand);
            for (const CaseInfo& c : sw.CasesUnsorted()) {
                size_t value = c.value - sw.min_value();
                DCHECK_LE(0u, value);
                DCHECK_LT(value + 2, input_count);
                inputs[value + 2] = g.Label(c.branch);
            }
            Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr);
        }

        void InstructionSelector::EmitLookupSwitch(const SwitchInfo& sw,
            InstructionOperand& value_operand)
        {
            OperandGenerator g(this);
            std::vector<CaseInfo> cases = sw.CasesSortedByOriginalOrder();
            size_t input_count = 2 + sw.case_count() * 2;
            DCHECK_LE(sw.case_count(), (std::numeric_limits<size_t>::max() - 2) / 2);
            auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
            inputs[0] = value_operand;
            inputs[1] = g.Label(sw.default_branch());
            for (size_t index = 0; index < cases.size(); ++index) {
                const CaseInfo& c = cases[index];
                inputs[index * 2 + 2 + 0] = g.TempImmediate(c.value);
                inputs[index * 2 + 2 + 1] = g.Label(c.branch);
            }
            Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr);
        }

        void InstructionSelector::EmitBinarySearchSwitch(
            const SwitchInfo& sw, InstructionOperand& value_operand)
        {
            OperandGenerator g(this);
            size_t input_count = 2 + sw.case_count() * 2;
            DCHECK_LE(sw.case_count(), (std::numeric_limits<size_t>::max() - 2) / 2);
            auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
            inputs[0] = value_operand;
            inputs[1] = g.Label(sw.default_branch());
            std::vector<CaseInfo> cases = sw.CasesSortedByValue();
            std::stable_sort(cases.begin(), cases.end(),
                [](CaseInfo a, CaseInfo b) { return a.value < b.value; });
            for (size_t index = 0; index < cases.size(); ++index) {
                const CaseInfo& c = cases[index];
                inputs[index * 2 + 2 + 0] = g.TempImmediate(c.value);
                inputs[index * 2 + 2 + 1] = g.Label(c.branch);
            }
            Emit(kArchBinarySearchSwitch, 0, nullptr, input_count, inputs, 0, nullptr);
        }

        void InstructionSelector::VisitBitcastTaggedToWord(Node* node)
        {
            EmitIdentity(node);
        }

        void InstructionSelector::VisitBitcastWordToTagged(Node* node)
        {
            OperandGenerator g(this);
            Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(node->InputAt(0)));
        }

// 32 bit targets do not implement the following instructions.
#if V8_TARGET_ARCH_32_BIT

        void InstructionSelector::VisitWord64And(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord64Or(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64Xor(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64Shl(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64Shr(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64Sar(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64Ror(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64Clz(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64Ctz(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64ReverseBits(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord64Popcnt(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64Equal(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitInt64Add(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitInt64AddWithOverflow(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitInt64Sub(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitInt64SubWithOverflow(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitInt64Mul(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitInt64Div(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitInt64LessThan(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitInt64LessThanOrEqual(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitUint64Div(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitInt64Mod(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitUint64LessThan(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitUint64LessThanOrEqual(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitUint64Mod(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitChangeInt32ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitChangeInt64ToFloat64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitChangeUint32ToUint64(Node* node)
        {
            UNIMPLEMENTED();
        }

// TODO(mips-team): Support compress pointers.
#ifdef V8_COMPRESS_POINTERS
        void InstructionSelector::VisitChangeTaggedToCompressed(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitChangeTaggedPointerToCompressedPointer(
            Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitChangeTaggedSignedToCompressedSigned(
            Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitChangeCompressedToTagged(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitChangeCompressedPointerToTaggedPointer(
            Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitChangeCompressedSignedToTaggedSigned(
            Node* node)
        {
            UNIMPLEMENTED();
        }
#endif

        void InstructionSelector::VisitChangeFloat64ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitChangeFloat64ToUint64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitTruncateFloat64ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitTryTruncateFloat64ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitTryTruncateFloat32ToUint64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitTruncateInt64ToInt32(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitRoundInt64ToFloat32(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitRoundInt64ToFloat64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitRoundUint64ToFloat32(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitRoundUint64ToFloat64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitBitcastFloat64ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitBitcastInt64ToFloat64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitSignExtendWord8ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitSignExtendWord16ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitSignExtendWord32ToInt64(Node* node)
        {
            UNIMPLEMENTED();
        }
#endif // V8_TARGET_ARCH_32_BIT

// 64 bit targets do not implement the following instructions.
#if V8_TARGET_ARCH_64_BIT
        void InstructionSelector::VisitInt32PairAdd(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitInt32PairSub(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitInt32PairMul(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord32PairShl(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord32PairShr(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord32PairSar(Node* node) { UNIMPLEMENTED(); }
#endif // V8_TARGET_ARCH_64_BIT

#if !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM && !V8_TARGET_ARCH_MIPS
        void InstructionSelector::VisitWord32AtomicPairLoad(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord32AtomicPairStore(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord32AtomicPairAdd(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord32AtomicPairSub(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord32AtomicPairAnd(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord32AtomicPairOr(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord32AtomicPairXor(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord32AtomicPairExchange(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord32AtomicPairCompareExchange(Node* node)
        {
            UNIMPLEMENTED();
        }
#endif // !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM && !V8_TARGET_ARCH_MIPS

#if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_S390 && !V8_TARGET_ARCH_PPC
        void InstructionSelector::VisitWord64AtomicLoad(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord64AtomicStore(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord64AtomicAdd(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64AtomicSub(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64AtomicAnd(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64AtomicOr(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64AtomicXor(Node* node) { UNIMPLEMENTED(); }

        void InstructionSelector::VisitWord64AtomicExchange(Node* node)
        {
            UNIMPLEMENTED();
        }

        void InstructionSelector::VisitWord64AtomicCompareExchange(Node* node)
        {
            UNIMPLEMENTED();
        }
#endif // !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_PPC \
    // !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_S390

        void InstructionSelector::VisitFinishRegion(Node* node)
        {
            EmitIdentity(node);
        }

        void InstructionSelector::VisitParameter(Node* node)
        {
            OperandGenerator g(this);
            int index = ParameterIndexOf(node->op());
            InstructionOperand op = linkage()->ParameterHasSecondaryLocation(index)
                ? g.DefineAsDualLocation(
                    node, linkage()->GetParameterLocation(index),
                    linkage()->GetParameterSecondaryLocation(index))
                : g.DefineAsLocation(node, linkage()->GetParameterLocation(index));

            Emit(kArchNop, op);
        }

        namespace {
            LinkageLocation ExceptionLocation()
            {
                return LinkageLocation::ForRegister(kReturnRegister0.code(),
                    MachineType::IntPtr());
            }
        } // namespace

        void InstructionSelector::VisitIfException(Node* node)
        {
            OperandGenerator g(this);
            DCHECK_EQ(IrOpcode::kCall, node->InputAt(1)->opcode());
            Emit(kArchNop, g.DefineAsLocation(node, ExceptionLocation()));
        }

        void InstructionSelector::VisitOsrValue(Node* node)
        {
            OperandGenerator g(this);
            int index = OsrValueIndexOf(node->op());
            Emit(kArchNop,
                g.DefineAsLocation(node, linkage()->GetOsrValueLocation(index)));
        }

        void InstructionSelector::VisitPhi(Node* node)
        {
            const int input_count = node->op()->ValueInputCount();
            DCHECK_EQ(input_count, current_block_->PredecessorCount());
            PhiInstruction* phi = new (instruction_zone())
                PhiInstruction(instruction_zone(), GetVirtualRegister(node),
                    static_cast<size_t>(input_count));
            sequence()
                ->InstructionBlockAt(RpoNumber::FromInt(current_block_->rpo_number()))
                ->AddPhi(phi);
            for (int i = 0; i < input_count; ++i) {
                Node* const input = node->InputAt(i);
                MarkAsUsed(input);
                phi->SetInput(static_cast<size_t>(i), GetVirtualRegister(input));
            }
        }

        void InstructionSelector::VisitProjection(Node* node)
        {
            OperandGenerator g(this);
            Node* value = node->InputAt(0);
            switch (value->opcode()) {
            case IrOpcode::kInt32AddWithOverflow:
            case IrOpcode::kInt32SubWithOverflow:
            case IrOpcode::kInt32MulWithOverflow:
            case IrOpcode::kInt64AddWithOverflow:
            case IrOpcode::kInt64SubWithOverflow:
            case IrOpcode::kTryTruncateFloat32ToInt64:
            case IrOpcode::kTryTruncateFloat64ToInt64:
            case IrOpcode::kTryTruncateFloat32ToUint64:
            case IrOpcode::kTryTruncateFloat64ToUint64:
            case IrOpcode::kInt32PairAdd:
            case IrOpcode::kInt32PairSub:
            case IrOpcode::kInt32PairMul:
            case IrOpcode::kWord32PairShl:
            case IrOpcode::kWord32PairShr:
            case IrOpcode::kWord32PairSar:
            case IrOpcode::kInt32AbsWithOverflow:
            case IrOpcode::kInt64AbsWithOverflow:
                if (ProjectionIndexOf(node->op()) == 0u) {
                    Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
                } else {
                    DCHECK_EQ(1u, ProjectionIndexOf(node->op()));
                    MarkAsUsed(value);
                }
                break;
            default:
                break;
            }
        }

        void InstructionSelector::VisitConstant(Node* node)
        {
            // We must emit a NOP here because every live range needs a defining
            // instruction in the register allocator.
            OperandGenerator g(this);
            Emit(kArchNop, g.DefineAsConstant(node));
        }

        void InstructionSelector::VisitCall(Node* node, BasicBlock* handler)
        {
            OperandGenerator g(this);
            auto call_descriptor = CallDescriptorOf(node->op());

            FrameStateDescriptor* frame_state_descriptor = nullptr;
            if (call_descriptor->NeedsFrameState()) {
                frame_state_descriptor = GetFrameStateDescriptor(
                    node->InputAt(static_cast<int>(call_descriptor->InputCount())));
            }

            CallBuffer buffer(zone(), call_descriptor, frame_state_descriptor);
            CallDescriptor::Flags flags = call_descriptor->flags();

            // Compute InstructionOperands for inputs and outputs.
            // TODO(turbofan): on some architectures it's probably better to use
            // the code object in a register if there are multiple uses of it.
            // Improve constant pool and the heuristics in the register allocator
            // for where to emit constants.
            CallBufferFlags call_buffer_flags(kCallCodeImmediate | kCallAddressImmediate);
            if (flags & CallDescriptor::kAllowCallThroughSlot) {
                // TODO(v8:6666): Remove kAllowCallThroughSlot and use a pc-relative call
                // instead once builtins are embedded in every build configuration.
                call_buffer_flags |= kAllowCallThroughSlot;
#ifndef V8_TARGET_ARCH_32_BIT
                // kAllowCallThroughSlot is only supported on ia32.
                UNREACHABLE();
#endif
            }
            InitializeCallBuffer(node, &buffer, call_buffer_flags, false);

            EmitPrepareArguments(&(buffer.pushed_nodes), call_descriptor, node);

            // Pass label of exception handler block.
            if (handler) {
                DCHECK_EQ(IrOpcode::kIfException, handler->front()->opcode());
                flags |= CallDescriptor::kHasExceptionHandler;
                buffer.instruction_args.push_back(g.Label(handler));
            }

            // Select the appropriate opcode based on the call type.
            InstructionCode opcode = kArchNop;
            switch (call_descriptor->kind()) {
            case CallDescriptor::kCallAddress:
                opcode = kArchCallCFunction | MiscField::encode(static_cast<int>(call_descriptor->ParameterCount()));
                break;
            case CallDescriptor::kCallCodeObject:
                opcode = kArchCallCodeObject | MiscField::encode(flags);
                break;
            case CallDescriptor::kCallJSFunction:
                opcode = kArchCallJSFunction | MiscField::encode(flags);
                break;
            case CallDescriptor::kCallWasmFunction:
            case CallDescriptor::kCallWasmImportWrapper:
                opcode = kArchCallWasmFunction | MiscField::encode(flags);
                break;
            case CallDescriptor::kCallBuiltinPointer:
                opcode = kArchCallBuiltinPointer | MiscField::encode(flags);
                break;
            }

            // Emit the call instruction.
            size_t const output_count = buffer.outputs.size();
            auto* outputs = output_count ? &buffer.outputs.front() : nullptr;
            Instruction* call_instr = Emit(opcode, output_count, outputs, buffer.instruction_args.size(),
                &buffer.instruction_args.front());
            if (instruction_selection_failed())
                return;
            call_instr->MarkAsCall();

            EmitPrepareResults(&(buffer.output_nodes), call_descriptor, node);
        }

        void InstructionSelector::VisitCallWithCallerSavedRegisters(
            Node* node, BasicBlock* handler)
        {
            OperandGenerator g(this);
            const auto fp_mode = CallDescriptorOf(node->op())->get_save_fp_mode();
            Emit(kArchSaveCallerRegisters | MiscField::encode(static_cast<int>(fp_mode)),
                g.NoOutput());
            VisitCall(node, handler);
            Emit(kArchRestoreCallerRegisters | MiscField::encode(static_cast<int>(fp_mode)),
                g.NoOutput());
        }

        void InstructionSelector::VisitTailCall(Node* node)
        {
            OperandGenerator g(this);
            auto call_descriptor = CallDescriptorOf(node->op());

            CallDescriptor* caller = linkage()->GetIncomingDescriptor();
            DCHECK(caller->CanTailCall(node));
            const CallDescriptor* callee = CallDescriptorOf(node->op());
            int stack_param_delta = callee->GetStackParameterDelta(caller);
            CallBuffer buffer(zone(), call_descriptor, nullptr);

            // Compute InstructionOperands for inputs and outputs.
            CallBufferFlags flags(kCallCodeImmediate | kCallTail);
            if (IsTailCallAddressImmediate()) {
                flags |= kCallAddressImmediate;
            }
            if (callee->flags() & CallDescriptor::kFixedTargetRegister) {
                flags |= kCallFixedTargetRegister;
            }
            DCHECK_EQ(callee->flags() & CallDescriptor::kAllowCallThroughSlot, 0);
            InitializeCallBuffer(node, &buffer, flags, true, stack_param_delta);

            // Select the appropriate opcode based on the call type.
            InstructionCode opcode;
            InstructionOperandVector temps(zone());
            if (linkage()->GetIncomingDescriptor()->IsJSFunctionCall()) {
                switch (call_descriptor->kind()) {
                case CallDescriptor::kCallCodeObject:
                    opcode = kArchTailCallCodeObjectFromJSFunction;
                    break;
                default:
                    UNREACHABLE();
                    return;
                }
                int temps_count = GetTempsCountForTailCallFromJSFunction();
                for (int i = 0; i < temps_count; i++) {
                    temps.push_back(g.TempRegister());
                }
            } else {
                switch (call_descriptor->kind()) {
                case CallDescriptor::kCallCodeObject:
                    opcode = kArchTailCallCodeObject;
                    break;
                case CallDescriptor::kCallAddress:
                    opcode = kArchTailCallAddress;
                    break;
                case CallDescriptor::kCallWasmFunction:
                    opcode = kArchTailCallWasm;
                    break;
                default:
                    UNREACHABLE();
                    return;
                }
            }
            opcode |= MiscField::encode(call_descriptor->flags());

            Emit(kArchPrepareTailCall, g.NoOutput());

            // Add an immediate operand that represents the first slot that is unused
            // with respect to the stack pointer that has been updated for the tail call
            // instruction. This is used by backends that need to pad arguments for stack
            // alignment, in order to store an optional slot of padding above the
            // arguments.
            int optional_padding_slot = callee->GetFirstUnusedStackSlot();
            buffer.instruction_args.push_back(g.TempImmediate(optional_padding_slot));

            int first_unused_stack_slot = (V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK ? true : false) + stack_param_delta;
            buffer.instruction_args.push_back(g.TempImmediate(first_unused_stack_slot));

            // Emit the tailcall instruction.
            Emit(opcode, 0, nullptr, buffer.instruction_args.size(),
                &buffer.instruction_args.front(), temps.size(),
                temps.empty() ? nullptr : &temps.front());
        }

        void InstructionSelector::VisitGoto(BasicBlock* target)
        {
            // jump to the next block.
            OperandGenerator g(this);
            Emit(kArchJmp, g.NoOutput(), g.Label(target));
        }

        void InstructionSelector::VisitReturn(Node* ret)
        {
            OperandGenerator g(this);
            const int input_count = linkage()->GetIncomingDescriptor()->ReturnCount() == 0
                ? 1
                : ret->op()->ValueInputCount();
            DCHECK_GE(input_count, 1);
            auto value_locations = zone()->NewArray<InstructionOperand>(input_count);
            Node* pop_count = ret->InputAt(0);
            value_locations[0] = (pop_count->opcode() == IrOpcode::kInt32Constant || pop_count->opcode() == IrOpcode::kInt64Constant)
                ? g.UseImmediate(pop_count)
                : g.UseRegister(pop_count);
            for (int i = 1; i < input_count; ++i) {
                value_locations[i] = g.UseLocation(ret->InputAt(i), linkage()->GetReturnLocation(i - 1));
            }
            Emit(kArchRet, 0, nullptr, input_count, value_locations);
        }

        void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
            BasicBlock* fbranch)
        {
            if (NeedsPoisoning(IsSafetyCheckOf(branch->op()))) {
                FlagsContinuation cont = FlagsContinuation::ForBranchAndPoison(kNotEqual, tbranch, fbranch);
                VisitWordCompareZero(branch, branch->InputAt(0), &cont);
            } else {
                FlagsContinuation cont = FlagsContinuation::ForBranch(kNotEqual, tbranch, fbranch);
                VisitWordCompareZero(branch, branch->InputAt(0), &cont);
            }
        }

        void InstructionSelector::VisitDeoptimizeIf(Node* node)
        {
            DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
            if (NeedsPoisoning(p.is_safety_check())) {
                FlagsContinuation cont = FlagsContinuation::ForDeoptimizeAndPoison(
                    kNotEqual, p.kind(), p.reason(), p.feedback(), node->InputAt(1));
                VisitWordCompareZero(node, node->InputAt(0), &cont);
            } else {
                FlagsContinuation cont = FlagsContinuation::ForDeoptimize(
                    kNotEqual, p.kind(), p.reason(), p.feedback(), node->InputAt(1));
                VisitWordCompareZero(node, node->InputAt(0), &cont);
            }
        }

        void InstructionSelector::VisitDeoptimizeUnless(Node* node)
        {
            DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
            if (NeedsPoisoning(p.is_safety_check())) {
                FlagsContinuation cont = FlagsContinuation::ForDeoptimizeAndPoison(
                    kEqual, p.kind(), p.reason(), p.feedback(), node->InputAt(1));
                VisitWordCompareZero(node, node->InputAt(0), &cont);
            } else {
                FlagsContinuation cont = FlagsContinuation::ForDeoptimize(
                    kEqual, p.kind(), p.reason(), p.feedback(), node->InputAt(1));
                VisitWordCompareZero(node, node->InputAt(0), &cont);
            }
        }

        void InstructionSelector::VisitTrapIf(Node* node, TrapId trap_id)
        {
            FlagsContinuation cont = FlagsContinuation::ForTrap(kNotEqual, trap_id, node->InputAt(1));
            VisitWordCompareZero(node, node->InputAt(0), &cont);
        }

        void InstructionSelector::VisitTrapUnless(Node* node, TrapId trap_id)
        {
            FlagsContinuation cont = FlagsContinuation::ForTrap(kEqual, trap_id, node->InputAt(1));
            VisitWordCompareZero(node, node->InputAt(0), &cont);
        }

        void InstructionSelector::EmitIdentity(Node* node)
        {
            OperandGenerator g(this);
            MarkAsUsed(node->InputAt(0));
            SetRename(node, node->InputAt(0));
        }

        void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind,
            DeoptimizeReason reason,
            VectorSlotPair const& feedback,
            Node* value)
        {
            EmitDeoptimize(kArchDeoptimize, 0, nullptr, 0, nullptr, kind, reason,
                feedback, value);
        }

        void InstructionSelector::VisitThrow(Node* node)
        {
            OperandGenerator g(this);
            Emit(kArchThrowTerminator, g.NoOutput());
        }

        void InstructionSelector::VisitDebugBreak(Node* node)
        {
            OperandGenerator g(this);
            Emit(kArchDebugBreak, g.NoOutput());
        }

        void InstructionSelector::VisitUnreachable(Node* node)
        {
            OperandGenerator g(this);
            Emit(kArchDebugBreak, g.NoOutput());
        }

        void InstructionSelector::VisitDeadValue(Node* node)
        {
            OperandGenerator g(this);
            MarkAsRepresentation(DeadValueRepresentationOf(node->op()), node);
            Emit(kArchDebugBreak, g.DefineAsConstant(node));
        }

        void InstructionSelector::VisitComment(Node* node)
        {
            OperandGenerator g(this);
            InstructionOperand operand(g.UseImmediate(node));
            Emit(kArchComment, 0, nullptr, 1, &operand);
        }

        void InstructionSelector::VisitUnsafePointerAdd(Node* node)
        {
#if V8_TARGET_ARCH_64_BIT
            VisitInt64Add(node);
#else // V8_TARGET_ARCH_64_BIT
            VisitInt32Add(node);
#endif // V8_TARGET_ARCH_64_BIT
        }

        void InstructionSelector::VisitRetain(Node* node)
        {
            OperandGenerator g(this);
            Emit(kArchNop, g.NoOutput(), g.UseAny(node->InputAt(0)));
        }

        bool InstructionSelector::CanProduceSignalingNaN(Node* node)
        {
            // TODO(jarin) Improve the heuristic here.
            if (node->opcode() == IrOpcode::kFloat64Add || node->opcode() == IrOpcode::kFloat64Sub || node->opcode() == IrOpcode::kFloat64Mul) {
                return false;
            }
            return true;
        }

        FrameStateDescriptor* InstructionSelector::GetFrameStateDescriptor(
            Node* state)
        {
            DCHECK_EQ(IrOpcode::kFrameState, state->opcode());
            DCHECK_EQ(kFrameStateInputCount, state->InputCount());
            FrameStateInfo state_info = FrameStateInfoOf(state->op());

            int parameters = static_cast<int>(
                StateValuesAccess(state->InputAt(kFrameStateParametersInput)).size());
            int locals = static_cast<int>(
                StateValuesAccess(state->InputAt(kFrameStateLocalsInput)).size());
            int stack = static_cast<int>(
                StateValuesAccess(state->InputAt(kFrameStateStackInput)).size());

            DCHECK_EQ(parameters, state_info.parameter_count());
            DCHECK_EQ(locals, state_info.local_count());

            FrameStateDescriptor* outer_state = nullptr;
            Node* outer_node = state->InputAt(kFrameStateOuterStateInput);
            if (outer_node->opcode() == IrOpcode::kFrameState) {
                outer_state = GetFrameStateDescriptor(outer_node);
            }

            return new (instruction_zone()) FrameStateDescriptor(
                instruction_zone(), state_info.type(), state_info.bailout_id(),
                state_info.state_combine(), parameters, locals, stack,
                state_info.shared_info(), outer_state);
        }

        // static
        void InstructionSelector::CanonicalizeShuffle(bool inputs_equal,
            uint8_t* shuffle,
            bool* needs_swap,
            bool* is_swizzle)
        {
            *needs_swap = false;
            // Inputs equal, then it's a swizzle.
            if (inputs_equal) {
                *is_swizzle = true;
            } else {
                // Inputs are distinct; check that both are required.
                bool src0_is_used = false;
                bool src1_is_used = false;
                for (int i = 0; i < kSimd128Size; ++i) {
                    if (shuffle[i] < kSimd128Size) {
                        src0_is_used = true;
                    } else {
                        src1_is_used = true;
                    }
                }
                if (src0_is_used && !src1_is_used) {
                    *is_swizzle = true;
                } else if (src1_is_used && !src0_is_used) {
                    *needs_swap = true;
                    *is_swizzle = true;
                } else {
                    *is_swizzle = false;
                    // Canonicalize general 2 input shuffles so that the first input lanes are
                    // encountered first. This makes architectural shuffle pattern matching
                    // easier, since we only need to consider 1 input ordering instead of 2.
                    if (shuffle[0] >= kSimd128Size) {
                        // The second operand is used first. Swap inputs and adjust the shuffle.
                        *needs_swap = true;
                        for (int i = 0; i < kSimd128Size; ++i) {
                            shuffle[i] ^= kSimd128Size;
                        }
                    }
                }
            }
            if (*is_swizzle) {
                for (int i = 0; i < kSimd128Size; ++i)
                    shuffle[i] &= kSimd128Size - 1;
            }
        }

        void InstructionSelector::CanonicalizeShuffle(Node* node, uint8_t* shuffle,
            bool* is_swizzle)
        {
            // Get raw shuffle indices.
            memcpy(shuffle, OpParameter<uint8_t*>(node->op()), kSimd128Size);
            bool needs_swap;
            bool inputs_equal = GetVirtualRegister(node->InputAt(0)) == GetVirtualRegister(node->InputAt(1));
            CanonicalizeShuffle(inputs_equal, shuffle, &needs_swap, is_swizzle);
            if (needs_swap) {
                SwapShuffleInputs(node);
            }
            // Duplicate the first input; for some shuffles on some architectures, it's
            // easiest to implement a swizzle as a shuffle so it might be used.
            if (*is_swizzle) {
                node->ReplaceInput(1, node->InputAt(0));
            }
        }

        // static
        void InstructionSelector::SwapShuffleInputs(Node* node)
        {
            Node* input0 = node->InputAt(0);
            Node* input1 = node->InputAt(1);
            node->ReplaceInput(0, input1);
            node->ReplaceInput(1, input0);
        }

        // static
        bool InstructionSelector::TryMatchIdentity(const uint8_t* shuffle)
        {
            for (int i = 0; i < kSimd128Size; ++i) {
                if (shuffle[i] != i)
                    return false;
            }
            return true;
        }

        // static
        bool InstructionSelector::TryMatch32x4Shuffle(const uint8_t* shuffle,
            uint8_t* shuffle32x4)
        {
            for (int i = 0; i < 4; ++i) {
                if (shuffle[i * 4] % 4 != 0)
                    return false;
                for (int j = 1; j < 4; ++j) {
                    if (shuffle[i * 4 + j] - shuffle[i * 4 + j - 1] != 1)
                        return false;
                }
                shuffle32x4[i] = shuffle[i * 4] / 4;
            }
            return true;
        }

        // static
        bool InstructionSelector::TryMatch16x8Shuffle(const uint8_t* shuffle,
            uint8_t* shuffle16x8)
        {
            for (int i = 0; i < 8; ++i) {
                if (shuffle[i * 2] % 2 != 0)
                    return false;
                for (int j = 1; j < 2; ++j) {
                    if (shuffle[i * 2 + j] - shuffle[i * 2 + j - 1] != 1)
                        return false;
                }
                shuffle16x8[i] = shuffle[i * 2] / 2;
            }
            return true;
        }

        // static
        bool InstructionSelector::TryMatchConcat(const uint8_t* shuffle,
            uint8_t* offset)
        {
            // Don't match the identity shuffle (e.g. [0 1 2 ... 15]).
            uint8_t start = shuffle[0];
            if (start == 0)
                return false;
            DCHECK_GT(kSimd128Size, start); // The shuffle should be canonicalized.
            // A concatenation is a series of consecutive indices, with at most one jump
            // in the middle from the last lane to the first.
            for (int i = 1; i < kSimd128Size; ++i) {
                if ((shuffle[i]) != ((shuffle[i - 1] + 1))) {
                    if (shuffle[i - 1] != 15)
                        return false;
                    if (shuffle[i] % kSimd128Size != 0)
                        return false;
                }
            }
            *offset = start;
            return true;
        }

        // static
        bool InstructionSelector::TryMatchBlend(const uint8_t* shuffle)
        {
            for (int i = 0; i < 16; ++i) {
                if ((shuffle[i] & 0xF) != i)
                    return false;
            }
            return true;
        }

        // static
        int32_t InstructionSelector::Pack4Lanes(const uint8_t* shuffle)
        {
            int32_t result = 0;
            for (int i = 3; i >= 0; --i) {
                result <<= 8;
                result |= shuffle[i];
            }
            return result;
        }

        bool InstructionSelector::NeedsPoisoning(IsSafetyCheck safety_check) const
        {
            switch (poisoning_level_) {
            case PoisoningMitigationLevel::kDontPoison:
                return false;
            case PoisoningMitigationLevel::kPoisonAll:
                return safety_check != IsSafetyCheck::kNoSafetyCheck;
            case PoisoningMitigationLevel::kPoisonCriticalOnly:
                return safety_check == IsSafetyCheck::kCriticalSafetyCheck;
            }
            UNREACHABLE();
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
